Comptage DISTINCT sur plusieurs colonnes
Est-il une meilleure façon de faire une requête comme ceci:
SELECT COUNT(*)
FROM (SELECT DISTINCT DocumentId, DocumentSessionId
FROM DocumentOutputItems) AS internalQuery
je dois compter le nombre d'articles distincts de ce tableau, mais le distinct est sur deux colonnes.
ma requête fonctionne bien mais je me demandais si je pouvais obtenir le résultat final en utilisant juste une requête (sans utiliser une sous-requête)
16 réponses
si vous essayez d'améliorer la performance, vous pouvez essayer de créer une colonne persisted computed sur soit un hachage ou concatenated valeur des deux colonnes.
une fois qu'il est persisté, à condition que la colonne est déterministe et que vous utilisez des paramètres de base de données "sains", il peut être indexé et / ou des statistiques peuvent être créées sur elle.
je crois qu'un nombre distinct de la colonne calculée serait équivalent à votre requête.
Edit: Modifié à partir de la moins-que-la fiabilité de la somme de contrôle seule requête J'ai découvert une façon de faire cela (dans SQL Server 2005) qui fonctionne assez bien pour moi et je peux utiliser autant de colonnes que j'ai besoin (en les ajoutant à la fonction CHECKSUM ()). La fonction REVERSE () transforme les ints en varchars pour rendre le distinct plus fiable ""
SELECT COUNT(DISTINCT (CHECKSUM(DocumentId,DocumentSessionId)) + CHECKSUM(REVERSE(DocumentId),REVERSE(DocumentSessionId)) )
FROM DocumentOutPutItems
Qu'est-ce que votre requête existante que vous n'aimez pas? Si vous êtes préoccupé que DISTINCT
à travers deux colonnes ne renvoie pas seulement les permutations uniques pourquoi ne pas essayer?
cela fonctionne certainement comme vous pourriez vous attendre dans Oracle.
SQL> select distinct deptno, job from emp
2 order by deptno, job
3 /
DEPTNO JOB
---------- ---------
10 CLERK
10 MANAGER
10 PRESIDENT
20 ANALYST
20 CLERK
20 MANAGER
30 CLERK
30 MANAGER
30 SALESMAN
9 rows selected.
SQL> select count(*) from (
2 select distinct deptno, job from emp
3 )
4 /
COUNT(*)
----------
9
SQL>
modifier
je suis allé dans une impasse avec des analyses, mais la réponse était déprimante évidente...
SQL> select count(distinct concat(deptno,job)) from emp
2 /
COUNT(DISTINCTCONCAT(DEPTNO,JOB))
---------------------------------
9
SQL>
edit 2
étant donné les données suivantes, la solution de concaténation fournie ci-dessus ne tiendra pas compte:
col1 col2
---- ----
A AA
AA A
donc nous devons inclure un séparateur...
select col1 + '*' + col2 from t23
/
Évidemment, le séparateur choisi doit être un caractère ou ensemble de caractères, qui ne peut jamais apparaître dans la colonne.
Que Diriez-vous de quelque chose comme:
select count(*) from (select count(*) cnt from DocumentOutputItems group by DocumentId, DocumentSessionId) t1
fait probablement juste la même chose que vous êtes déjà bien que, mais il évite le DISTINCT.
pour exécuter une requête simple, concaténer les colonnes, puis obtenir le nombre distinct d'instances de la chaîne concaténée.
SELECT count(DISTINCT concat(DocumentId, DocumentSessionId)) FROM DocumentOutputItems;
dans MySQL vous pouvez faire la même chose sans l'étape de concaténation comme suit:
SELECT count(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems;
cette fonctionnalité est mentionnée dans la documentation MySQL:
http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count-distinct
Voici une version plus courte sans le sous-select:
SELECT COUNT(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems
cela fonctionne très bien à MySQL, et je pense que l'optimiseur a un temps plus facile de comprendre celui-ci.
Edit: apparemment j'ai mal lu MSSQL et MySQL - désolé pour ça, mais peut-être que ça aide quand même.
j'ai trouvé ceci quand J'ai googlé pour mon propre problème, trouvé que si vous comptez les objets distincts, vous obtenez le nombre correct retourné (J'utilise MySQL)
SELECT COUNT(DISTINCT DocumentID) AS Count1,
COUNT(DISTINCT DocumentSessionId) AS Count2
FROM DocumentOutputItems
il n'y a rien de mal à votre requête, mais vous pouvez aussi le faire de cette façon:
WITH internalQuery (Amount)
AS
(
SELECT (0)
FROM DocumentOutputItems
GROUP BY DocumentId, DocumentSessionId
)
SELECT COUNT(*) AS NumberOfDistinctRows
FROM internalQuery
si vous n'aviez qu'un champ pour "distinguer", vous pourriez utiliser:
SELECT COUNT(DISTINCT DocumentId)
FROM DocumentOutputItems
et qui renvoie le même plan de requête que l'original, tel que Testé avec SET SHOWPLAN_ALL on. Cependant, vous utilisez deux champs pour essayer quelque chose de fou comme:
SELECT COUNT(DISTINCT convert(varchar(15),DocumentId)+'|~|'+convert(varchar(15), DocumentSessionId))
FROM DocumentOutputItems
mais vous aurez des problèmes si NULLs sont impliqués. Je m'en tiendrais à la requête originale.
Espère que cela fonctionne, je suis en train d'écrire sur le prima vista
SELECT COUNT(*)
FROM DocumentOutputItems
GROUP BY DocumentId, DocumentSessionId
j'aimerais que Mme SQL puisse aussi faire quelque chose comme COUNT(DISTINCT A, B). Mais c'est impossible.
au début, la réponse de JayTee m'a semblé être une solution, mais après quelques tests, CHECKSUM() n'a pas réussi à créer des valeurs uniques. Un exemple rapide est, à la fois CHECKSUM(31,467,519) et CHECKSUM(69,1120,823) donne la même réponse qui est 55.
puis j'ai fait quelques recherches et j'ai constaté que Microsoft ne recommande pas l'utilisation de CHECKSUM pour la détection de changement. Certaines certains ont suggéré d'utiliser
SELECT COUNT(DISTINCT CHECKSUM(value1, value2, ..., valueN) + CHECKSUM(valueN, value(N-1), ..., value1))
mais ce n'est pas non plus convaincant.
vous pouvez utiliser la fonction HASHBYTES() comme suggéré dans TSQL CHECKSUM conundrum . Toutefois, cela a également une petite chance de ne pas retourner des résultats uniques.
je suggérerais d'utiliser
SELECT COUNT(DISTINCT CAST(DocumentId AS VARCHAR)+'-'+CAST(DocumentSessionId AS VARCHAR)) FROM DocumentOutputItems
beaucoup (la plupart?) Les bases de données SQL peuvent fonctionner avec des valeurs comme tuples donc vous pouvez juste faire:
SELECT COUNT(DISTINCT (DocumentId, DocumentSessionId))
FROM DocumentOutputItems;
Si votre base de données ne supporte pas cela, il peut être simulé selon la suggestion de CHECKSUM de @oncel-umut-turer ou d'une autre fonction scalaire fournissant une bonne unicité par exemple
COUNT(DISTINCT CONCAT(DocumentId, ':', DocumentSessionId))
.
une utilisation connexe de tuples est d'effectuer des requêtes IN
telles que:
SELECT * FROM DocumentOutputItems
WHERE (DocumentId, DocumentSessionId) in (('a', '1'), ('b', '2'));
ça me va. Dans oracle:
SELECT SUM(DECODE(COUNT(*),1,1,1))
FROM DocumentOutputItems GROUP BY DocumentId, DocumentSessionId;
en jpql:
SELECT SUM(CASE WHEN COUNT(i)=1 THEN 1 ELSE 1 END)
FROM DocumentOutputItems i GROUP BY i.DocumentId, i.DocumentSessionId;
que dis-tu de ça,
Select DocumentId, DocumentSessionId, count(*) as c
from DocumentOutputItems
group by DocumentId, DocumentSessionId;
cela nous donnera le compte de toutes les combinaisons possibles de DocumentId, et DocumentSessionId
vous pouvez simplement utiliser la fonction Count deux fois.
Dans ce cas, ce serait:
sélectionnez COUNT (DISTINCT DocumentId), COUNT (DISTINCT DocumentSessionId) FROM DocumentOutputItems
cela a été posé et répondu sur Quora ( https://www.quora.com/In-SQL-how-to-I-count-DISTINCT-over-multiple-columns ):
select col1, col2, col3, count(*)
from table
group by col1, col2, col3
je travaillais sur ce dans SAS, et SAS PROC SQL n'aime pas DISTINCT avec plus d'une colonne.