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
unest vraiment un sur-ensemble? c'est-à-dire que toutes les personnes dansbsont également dansa?Ouais bon appel, titre de la question édité