339 votes

Comptage DISTINCT sur plusieurs colonnes

Y a-t-il une meilleure façon de faire une requête comme celle-ci :

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

J'ai besoin de compter le nombre d'éléments distincts de cette table mais la distinction se fait sur deux colonnes.

Ma requête fonctionne bien mais je me demandais si je pouvais obtenir le résultat final en utilisant juste une seule requête (sans utiliser de sous-requête)

0 votes

Iordan Tanev, Mark Brackett, RC - merci pour vos réponses, c'était bien essayé, mais vous devez vérifier ce que vous faites avant de publier 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 alors que votre requête renvoie plusieurs lignes.

0 votes

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

0 votes

102voto

JasonHorner Points 1474

Si vous essayez d'améliorer les performances, vous pourriez essayer de créer une colonne calculée persistante sur une valeur de hachage ou 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 dessus.

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

6 votes

Excellente suggestion! Plus je lis, plus je réalise que SQL est moins une question de connaître la syntaxe et les fonctions et plus une question d'appliquer une logique pure.. J'aimerais avoir 2 votes !

2 votes

Trop bonne suggestion. Cela m'a évité d'écrire du code inutile à ce sujet.

6 votes

Voudriez-vous ajouter un exemple ou un extrait de code pour en montrer davantage sur ce que cela signifie et comment le faire?

65voto

JayTee Points 1584

Éditer : Modifié à partir de la requête peu fiable de vérification seulement J'ai découvert un moyen de le faire (dans SQL Server 2005) qui fonctionne assez bien pour moi et je peux utiliser autant de colonnes que nécessaire (en les ajoutant à la fonction CHECKSUM()). La fonction REVERSE() transforme les entiers en varchars pour rendre la distincte plus fiable

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

2 votes

+1 Beau travail, parfaitement fonctionnel (lorsque vous avez les bons types de colonnes pour effectuer un CheckSum sur... ;)

13 votes

Avec les hachages comme Checksum(), il y a une petite chance que le même hachage soit renvoyé pour des entrées différentes, donc le décompte peut être très légèrement inexact. HashBytes() a une chance encore plus faible mais ce n'est pas zéro. Si ces deux Id étaient des int's (32b), alors un "hachage sans perte" pourrait les combiner en un bigint (64b) comme Id1 << 32 + Id2.

3 votes

La chance n'est pas si petite en réalité, surtout lorsque vous commencez à combiner des colonnes (ce qui était censé être le but). J'ai été curieux de cette approche et dans un cas particulier, le code de contrôle s'est retrouvé avec un nombre 10 % plus petit. Si vous y réfléchissez un peu plus longtemps, le code de contrôle ne renvoie qu'un entier, donc si vous codez un code de contrôle de plage bigint complète, vous obtiendrez un nombre distinct d'environ 2 milliards de fois plus petit qu'il ne l'est réellement. -1

49voto

APC Points 69630

Qu'est-ce qui vous déplaît dans votre requête existante? Si vous craignez que DISTINCT sur deux colonnes ne renvoie pas seulement les permutations uniques, pourquoi ne pas essayer?

Cela fonctionne certainement comme vous pourriez 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

J'ai suivi une impasse avec l'analyse mais la réponse était évidente et 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 comptera erroneéent :

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 un ensemble de caractères, qui ne peut jamais apparaître dans l'une ou l'autre colonne.

1 votes

+1 de ma part. Merci pour ta réponse. Ma requête fonctionne bien mais je me demandais si je pouvais obtenir le résultat final en n'utilisant qu'une seule requête (sans utiliser de sous-requête)

21voto

Trevor Tippins Points 2400

Que diriez-vous de quelque chose comme :

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

Probablement fait juste la même chose que vous faites déjà mais cela évite le DISTINCT.

0 votes

Dans mes tests (en utilisant SET SHOWPLAN_ALL ON), il avait le même plan d'exécution et exactement le même coût total du sous-arbre.

2 votes

Selon la complexité de la requête initiale, résoudre cela avec GROUP BY peut introduire quelques défis supplémentaires à la transformation de la requête pour obtenir le résultat souhaité (par exemple, lorsque la requête initiale avait déjà des clauses GROUP BY ou HAVING...)

1voto

tehaugmenter Points 41

J'ai trouvé cela lorsque j'ai fait une recherche sur Google pour mon propre problème, j'ai constaté que si vous comptez les objets DISTINCT, vous obtenez le bon nombre retourné (j'utilise MySQL)

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

7 votes

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X