2009-09-24 14 views
133

Y at-il une meilleure façon de faire une requête comme ceci:Compter sur plusieurs colonnes DISTINCT

SELECT COUNT(*) 
FROM (SELECT DISTINCT DocumentId, DocumentSessionId 
     FROM DocumentOutputItems) AS internalQuery 

je dois compter le nombre d'éléments distincts de cette table mais distincte est sur deux colonnes.

Ma requête fonctionne bien, mais je me demandais si je peux obtenir le résultat final en utilisant une seule requête (sans l'aide d'un sous-requête)

+0

IordanTanev, Mark Brackett, RC - merci pour les réponses, c'était un bon essai, mais vous devez vérifier ce que vous faites avant de poster sur SO. Les requêtes que vous avez fournies ne sont pas équivalentes à ma requête. Vous pouvez facilement voir que j'ai toujours un résultat scalaire, mais votre requête renvoie plusieurs lignes. – Novitzky

+0

Juste mis à jour la question pour inclure votre commentaire de clarification d'une des réponses – Jeff

Répondre

45

Si vous essayez d'améliorer les performances, vous pourriez essayer de créer une colonne calculée persistante soit sur une table de hachage ou de la valeur concaténée des deux colonnes. Une fois qu'elle est persistante, à condition que la colonne soit déterministe et que vous utilisiez des paramètres de base de données "sains", elle peut être indexée et/ou des statistiques peuvent être créées.

Je crois qu'un nombre distinct de la colonne calculée serait équivalent à votre requête.

+3

Excellente suggestion! Le plus Je lis, plus je me rends compte que SQL est moins sur la connaissance de la syntaxe et des fonctions et plus sur l'application de la logique pure .. Je voudrais avoir 2 upvotes! – tumchaaditya

+0

Trop bonne suggestion.Il m'a évité d'écrire du code inutile. –

2

espère que cela fonctionne, je suis en train d'écrire sur vista prima

SELECT COUNT(*) 
FROM DocumentOutputItems 
GROUP BY DocumentId, DocumentSessionId 
+7

Pour que cela donne la réponse finale, vous devez l'envelopper dans un autre compte SELECT (*) DE (...). Essentiellement, cette réponse vous donne simplement un autre moyen d'énumérer les valeurs distinctes que vous voulez compter. Ce n'est pas mieux que votre solution originale. –

+0

Merci Dave. Je sais que vous pouvez utiliser groupe par plutôt que distinct dans mon cas. Je me demandais si vous obtenez le résultat final en utilisant une seule requête. Je pense que c'est impossible mais j'ai peut-être tort. – Novitzky

16

Qu'est-ce que votre requête existante n'aime pas? Si vous êtes concerné que DISTINCT à travers deux colonnes ne retourne pas seulement les permutations uniques pourquoi ne pas l'essayer?

Cela fonctionne certainement comme vous pouvez vous y 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 l'analyse, mais la réponse était évidente déprimante ...

SQL> select count(distinct concat(deptno,job)) from emp 
    2/

COUNT(DISTINCTCONCAT(DEPTNO,JOB)) 
--------------------------------- 
           9 

SQL> 

modifier 2

Compte tenu des données suivantes la solution de concaténation fournie ci-dessus sera erronée:

col1 col2 
---- ---- 
A  AA 
AA A 

Nous pour inclure un séparateur ...

select col1 + '*' + col2 from t23 
/

Il est évident que le séparateur choisi doit être un caractère ou un ensemble de caractères, qui ne peut jamais apparaître dans les deux colonnes.

+0

+1 de moi. Merci pour votre réponse. Ma requête fonctionne bien mais je me demandais si je pouvais obtenir le résultat final en utilisant une seule requête (sans utiliser de sous-requête) – Novitzky

11

Que diriez-vous quelque chose comme:

 
select count(*) 
from 
    (select count(*) cnt 
    from DocumentOutputItems 
    group by DocumentId, DocumentSessionId) t1 

probablement fait la même chose que vous êtes déjà bien, mais il évite les DISTINCT.

+0

Oui, c'est vrai. Il fait le même travail que mon original. – Novitzky

+0

dans mes tests (en utilisant SET SHOWPLAN_ALL ON), il avait le même plan d'exécution et exactement le même TotalSubtreeCost –

+0

+1 pour un bon essai et l'explication. – Novitzky

6

est ici une version plus courte sans subselect:

SELECT COUNT(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems 

Il fonctionne très bien dans MySQL, et je pense que l'optimiseur a un temps plus facile de comprendre celui-ci. Edit: Apparemment, j'ai mal interprété MSSQL et MySQL - désolé à ce sujet, mais peut-être que ça aide quand même.

+5

dans SQL Server vous obtenez: __Msg 102, Niveau 15, État 1, Ligne 1 Syntaxe incorrecte près de ',' .__ –

+0

C'est ce à quoi je pensais. Je veux faire une chose similaire dans MSSQL si possible. – Novitzky

+0

@Kamil Nowicki, dans SQL Server, vous ne pouvez avoir qu'un seul champ dans un COUNT(), dans ma réponse, je montre que vous pouvez concaténer les deux champs en un et essayer cette approche. Cependant, je voudrais juste coller avec l'original puisque les plans de requête finiraient par le même. –

2

si vous aviez un seul champ à « DISTINCT », vous pouvez utiliser:

SELECT COUNT(DISTINCT DocumentId) 
FROM DocumentOutputItems 

et qui ne 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 que vous puissiez 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 resterais avec la requête originale.

+0

+1 de moi.Merci mais je vais m'en tenir à ma requête comme vous l'avez suggéré.L'utilisation de "convertir" peut diminuer encore plus les performances – Novitzky

3

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 
41

Edit: Altered du moins que fiable checksum seule requête j'ai découvert une façon de le faire (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 la nette plus fiable

SELECT COUNT(DISTINCT (CHECKSUM(DocumentId,DocumentSessionId)) + CHECKSUM(REVERSE(DocumentId),REVERSE(DocumentSessionId))) 
FROM DocumentOutPutItems 
+0

+ 1 Nice, fonctionne parfaitement (quand vous avez les bons types de colonne pour effectuer un CheckSum sur ...;) –

+7

Avec les hachages comme Checksum(), il y a peu de chances que le même hachage soit retourné pour différentes entrées de sorte que le nombre peut être très légèrement désactivé. HashBytes() est une chance encore plus petite mais toujours pas zéro. Si ces deux Id étaient int (32b) alors un "hash sans perte" pourrait les combiner en un bigint (64b) comme Id1 << 32 + Id2. – crokusek

+1

la chance n'est pas si petite même, surtout quand vous commencez à combiner des colonnes (ce qui était censé être destiné). J'étais curieux de cette approche et dans un cas particulier la somme de contrôle a fini avec un compte 10% plus petit. Si vous y réfléchissez un peu plus longtemps, Checksum retourne juste un int, donc si vous avez une somme de contrôle complète, vous obtiendrez un compte distinct d'environ 2 milliards de fois plus petit qu'il n'y en a en réalité. -1 – pvolders

1

Je souhaite MS SQL pourrait aussi faire quelque chose comme COUNT (DISTINCT A, B). Mais ça ne peut pas. Au début, la réponse de JayTee me semblait être une solution après quelques tests CHECKSUM() n'a pas réussi à créer des valeurs uniques. Un exemple rapide est, CHECKSUM (31, 467, 519) et CHECKSUM (69, 1120, 823) donne la même réponse qui est 55.

Ensuite, j'ai fait quelques recherches et constaté que Microsoft ne recommande pas d'utiliser CHECKSUM à des fins de détection de changement. Dans certains forums 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 réconfortant.

Vous pouvez utiliser la fonction HASHBYTES() comme suggéré dans TSQL CHECKSUM conundrum. Cependant, cela a également une petite chance de ne pas retourner des résultats uniques.

Je suggère d'utiliser

SELECT COUNT(DISTINCT CAST(DocumentId AS VARCHAR)+'-'+CAST(DocumentSessionId AS VARCHAR)) FROM DocumentOutputItems 
5

J'ai trouvé quand je googlé pour mon propre problème, constaté que si vous comptez les objets DISTINCTS, vous obtenez le bon numéro retourné (j'utilise MySQL)

SELECT COUNT(DISTINCT DocumentID) AS Count1, 
    COUNT(DISTINCT DocumentSessionId) AS Count2 
    FROM DocumentOutputItems 
+4

La requête ci-dessus va retourner un ensemble de résultats différent de ce que l'OP recherchait (les combinaisons distinctes ** ** de 'DocumentId' et ' DocumentSessionId'). Alexander Kjäll déjà posté la bonne réponse si l'OP utilisait MySQL et non MS SQL Server. –

4

Pour exécuter en tant que requête unique, 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; 

En MySQL, vous pouvez faire la même chose sans l'étape de concaténation comme suit:

SELECT count(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems; 

Cette fonction est mentionné dans la documentation MySQL:

http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count-distinct

+0

Il s'agissait d'une question SQL Server et les deux options que vous avez publiées ont déjà été mentionnées dans les réponses suivantes à cette question: http://stackoverflow.com/a/1471444/4955425 et http://stackoverflow.com/a/1471713/4955425. – sstan

-2

Cela a été posé et a 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 cela dans SAS, et SAS Proc SQL n'aime pas DISTINCT avec plus d'une colonne.

+0

La requête d'origine dans la question renvoie le nombre de combinaisons dans des colonnes données. Cette réponse renvoie à la place le nombre d'occurrences pour chaque combinaison dans des colonnes données. – jumxozizi

-2

Cette requête ci-dessous a fonctionné pour moi sur MySQL:

SELECT COUNT(DISTINCT col_1,col_2,..) from table_name; 

Les colonnes données dans la requête ci-dessus à savoir. col_1, col_2 est associé à une contrainte UNIQUE. Cela signifie que dans mon tableau table_name, j'ai créé un index UNIQUE sur col_1 + col_2.

Questions connexes