Disons que j'ai une table table
sous la forme:
WITH a_users AS ( SELECT user, SUM(CASE WHEN class = 'a' THEN 1 ELSE 0 END) AS a_class FROM table WHERE class in ('a', 'b') GROUP BY user ), labeled_users as ( SELECT user, CASE WHEN a_class >=1 then 'a' ELSE 'b' END as label FROM a_users ) SELECT label, COUNT(DISTINCT user) FROM labeled_users;
Il n'y a que deux classes.
Je veux écrire une requête de telle sorte que nous comptions le nombre d'utilisateurs dans chaque classe de telle sorte que tout utilisateur qui a les étiquettes a
et b
soit trié dans a
, tout utilisateur avec seulement a
est trié dans a
, puis tout utilisateur avec seulement b
entre dans b . Si elle était appliquée à l'extrait de tableau ci-dessus, nous obtiendrions:
| a | b | |---|---| | 2 | 1 |
La transposition est également acceptable, comme:
| class | count | |-------|-------| | a | 2 | | b | 1 |
Mon La solution actuelle implique deux CTE:
| user | class | |------|-------| | 1 | a | | 1 | b | | 1 | b | | 2 | b | | 3 | a |
Existe-t-il un (1) moyen plus efficace de résoudre ce problème ou (2) une solution plus concise / lisible?
4 Réponses :
Quelque chose comme ça devrait fonctionner, si a
et b
sont vraiment vos classes. Sinon, ajustez le min / max au besoin.
; with CTE as ( Select user, min(class) as Class from Labeled_Users group by user) Select Class, count(*) from CTE group by Class
Ouais, je ne suppose pas que - faire semblant qu'ils sont deux chaînes différentes
La logique devrait toujours fonctionner, il vous suffit de choisir min / max selon les besoins pour la classe que vous souhaitez privilégier. Par exemple. si vos cours sont prioritaires
et pas prioritaires
alors utilisez max
au lieu de min
, puisque priorité code> trie moins de
pas de priorité
.
Voici une requête simple pour faire le travail en utilisant une sous-requête et une agrégation conditionnelle. Elle doit renvoyer la deuxième version de votre résultat attendu (pivoté):
SELECT SUM(CASE WHEN x.minc <> x.maxc OR x.maxc = 'a' THEN 1 ELSE 0 END) a, SUM(CASE WHEN x.minc = x.maxc AND x.maxc = 'b' THEN 1 ELSE 0 END) b FROM ( SELECT user, MAX(class) maxclass, MIN(class) minclass FROM mytable GROUP BY user ) x
La sous-requête calcule la classe minimum et maximum de chaque utilisateur. Ensuite, la requête externe compte séparément les utilisateurs:
a
: utilisateurs qui appartiennent aux deux classes ou simplement à la classe a
b
: utilisateurs appartenant à la classe b
uniquement Il s'agit d'une syntaxe SQL standard qui fonctionnera sur la plupart des SGBDR (évidemment, même ceux qui ne supportent pas les CTE, comme les versions MySQL antérieures à la 8.0).
En gros, vous voulez "a" pour un utilisateur qui a "a" du tout. Une sous-requête est la première approche:
select count(distinct case when class = 'a' then user end) as num_as, count(distinct user) - count(distinct case when class = 'a' then user end) as num_bs from t;
Avec une petite astuce, vous pouvez éliminer la sous-requête:
select sum(case when num_as > 0 then 1 else 0 end) as num_class_a, sum(case when num_as = 0 then 1 else 0 end) as num_class_b from (select user, sum(case when class = 'a' then 1 else 0 end) as num_as from t group by user ) t;
Hey! Vous avez oublié un trait de soulignement dans num_as
dans la deuxième ligne de votre premier extrait de code
Utilisation de String_agg()
:
with usr_class as( SELECT DISTINCT usr, string_agg(txt,':') as all_class FROM abc GROUP BY usr ) select count(usr), case when POSITION('a' in all_class)>0 THEN 'a' ELSE 'b' END AS CLASS FROM usr_class GROUP BY case when POSITION('a' in all_class)>0 THEN 'a' ELSE 'b' END;
Est-ce que
un
est vraiment un sur-ensemble? c'est-à-dire que toutes les personnes dansb
sont également dansa
?Ouais bon appel, titre de la question édité