Quoi de plus rapide, SELECT DISTINCT ou GROUP BY dans MySQL?


273

Si j'ai une table

CREATE TABLE users (
  id int(10) unsigned NOT NULL auto_increment,
  name varchar(255) NOT NULL,
  profession varchar(255) NOT NULL,
  employer varchar(255) NOT NULL,
  PRIMARY KEY  (id)
)

et je veux obtenir toutes les valeurs uniques de professionchamp, ce qui serait plus rapide (ou recommandé):

SELECT DISTINCT u.profession FROM users u

ou

SELECT u.profession FROM users u GROUP BY u.profession

?


2
Vous pouvez tester par vous-même aussi rapidement que poser la question. De manière irritante, il est presque impossible de construire un scénario dans lequel DISTINCT surpasse GROUP BY - ce qui est ennuyeux car clairement ce n'est pas le but de GROUP BY. Cependant, GROUP BY peut produire des résultats trompeurs, ce qui, à mon avis, est une raison suffisante pour l'éviter.
Strawberry

Il y a un autre doublon avec une réponse différente. voir MySql - Distinct vs Group By <<< il dit que GROUP BY est meilleur
kolunar

Veuillez voir ici si vous souhaitez mesurer la différence de temps entre DISTINCT et GROUP BY en exécutant votre requête.
kolunar

Réponses:


258

Ils sont essentiellement équivalents les uns aux autres (en fait, c'est ainsi que certaines bases de données sont implémentées DISTINCTsous le capot).

Si l'un d'eux est plus rapide, ce sera le cas DISTINCT. En effet, bien que les deux soient les mêmes, un optimiseur de requête devrait détecter le fait que vous GROUP BYne tirez parti d'aucun membre du groupe, uniquement de ses clés. DISTINCTrend cela explicite, vous pouvez donc vous en tirer avec un optimiseur légèrement plus stupide.

En cas de doute, testez!


76
DISTINCT ne sera plus rapide que si vous N'AVEZ PAS d'index (car il ne trie pas). Lorsque vous avez un index et qu'il est utilisé, ce sont des synonymes.
Quassnoi

10
La définition de DISTINCTet GROUP BYdiffèrent en ce sens DISTINCTn'a pas à trier la sortie, et GROUP BYpar défaut le fait. Cependant, dans MySQL, même un DISTINCT+ ORDER BYpeut toujours être plus rapide qu'un en GROUP BYraison des conseils supplémentaires pour l'optimiseur, comme expliqué par SquareCog.
rustyx

1
DISTINCT est beaucoup plus rapide avec de grandes quantités de données.
Pankaj Wanjari

7
J'ai testé cela et j'ai constaté que sur une colonne indexée, mysql, group by était environ 6 fois plus lent que distinct avec une requête assez compliquée. Il suffit d'ajouter cela en tant que point de données. Environ 100 000 lignes. Alors testez-le et voyez par vous-même.
Lizardx

voir MySql - Distinct vs Group By <<< il dit que GROUP BY est meilleur
kolunar

100

Si vous avez un index sur profession, ces deux sont des synonymes.

Si ce n'est pas le cas, utilisez DISTINCT.

GROUP BYen MySQLsortes résultats. Vous pouvez même faire:

SELECT u.profession FROM users u GROUP BY u.profession DESC

et obtenez vos professions triées dans l' DESCordre.

DISTINCTcrée une table temporaire et l'utilise pour stocker les doublons. GROUP BYfait de même, mais trie les résultats distincts par la suite.

Alors

SELECT DISTINCT u.profession FROM users u

est plus rapide, si vous n'avez pas d'index sur profession.


6
Vous pouvez ajouter ORDER BY NULLà GROUP BYpour éviter le tri.
Ariel

Encore plus lent même avec un regroupement par null
Thanh Trung

@ThanhTrung: qu'est-ce qui est plus lent que quoi?
Quassnoi

@Quassnoi groupby plus lent que distinct même en évitant le tri
Thanh Trung

Remarque: les qualificatifs de commande sur GROUP BY ont été dépréciés dans MySQL 8.
Matthew Lenz

18

Toutes les réponses ci-dessus sont correctes, pour le cas de DISTINCT sur une seule colonne vs GROUPE BY sur une seule colonne. Chaque moteur db a sa propre implémentation et optimisations, et si vous vous souciez de la très petite différence (dans la plupart des cas), alors vous devez tester contre un serveur spécifique ET une version spécifique! Comme les implémentations peuvent changer ...

MAIS, si vous sélectionnez plus d'une colonne dans la requête, le DISTINCT est essentiellement différent! Parce que dans ce cas, il comparera TOUTES les colonnes de toutes les lignes, au lieu d'une seule colonne.

Donc, si vous avez quelque chose comme:

// This will NOT return unique by [id], but unique by (id,name)
SELECT DISTINCT id, name FROM some_query_with_joins

// This will select unique by [id].
SELECT id, name FROM some_query_with_joins GROUP BY id

C'est une erreur courante de penser que le mot-clé DISTINCT distingue les lignes par la première colonne que vous avez spécifiée, mais le DISTINCT est un mot-clé général de cette manière.

Donc, les gens, vous devez faire attention à ne pas considérer les réponses ci-dessus comme correctes dans tous les cas ... Vous pourriez être confus et obtenir de mauvais résultats alors que tout ce que vous vouliez était d'optimiser!


3
Bien que cette question est à propos de MySQL , il convient de noter que la deuxième requête ne fonctionnera que dans MySQL. Presque tous les autres SGBD rejetteront la deuxième instruction car il s'agit d'une utilisation non valide de l'opérateur GROUP BY.
a_horse_with_no_name

Eh bien, "presque" est une définition problématique :-) Il serait beaucoup plus utile si vous déclarez un SGBD spécifique que vous avez testé pour voir qu'il génère une erreur pour cette instruction.
daniel.gindi

3
Postgres, Oracle, Firebird, DB2, SQL Server pour les débutants. MySQL: sqlfiddle.com/#!2/6897c/1 Postgres: sqlfiddle.com/#!12/6897c/1 Oracle: sqlfiddle.com/#!12/6897c/1 SQL Server: sqlfiddle.com/#!6/ 6897c / 1
a_horse_with_no_name

17

Optez pour le plus simple et le plus court si vous le pouvez - DISTINCT semble être plus ce que vous recherchez uniquement parce qu'il vous donnera EXACTEMENT la réponse dont vous avez besoin et seulement cela!


7

Grouper coûte cher que Distinct puisque Group by fait un tri sur le résultat tandis que distinct l'évite. Mais si vous voulez que group by donne le même résultat que distinct, donnez l' ordre par null .

SELECT DISTINCT u.profession FROM users u

est égal à

SELECT u.profession FROM users u GROUP BY u.profession order by null

est égal àSELECT profession FROM users GROUP BY profession

6

bien distinct peut être plus lent que grouper à certaines occasions dans les postgres (ne sais pas pour les autres dbs).

exemple testé:

postgres=# select count(*) from (select distinct i from g) a;

count 

10001
(1 row)

Time: 1563,109 ms

postgres=# select count(*) from (select i from g group by i) a;

count
10001
(1 row)

Time: 594,481 ms

http://www.pgsql.cz/index.php/PostgreSQL_SQL_Tricks_I

donc sois prudent ... :)


5

Il semble que les requêtes ne soient pas exactement les mêmes. Au moins pour MySQL.

Comparer:

  1. décrire un nom de produit distinct dans northwind.products
  2. décrire le nom du produit sélectionné dans northwind.products group by productname

La deuxième requête donne en outre "Utilisation de filesort" dans Extra.


1
Ils sont les mêmes en termes de ce qu'ils obtiennent, pas en termes de comment ils l'obtiennent. Un optimiseur idéal les exécuterait de la même manière, mais l'optimiseur MySQL n'est pas idéal. Sur la base de vos preuves, il semblerait que DISTINCT irait plus vite - O (n) vs O (n * log n).
SquareCog

Donc, "utiliser filesort" est essentiellement une mauvaise chose?
vava

Dans ce cas, c'est parce que vous n'avez pas besoin de trier (vous le feriez si vous aviez besoin des groupes). MySQL trie afin de regrouper les mêmes entrées, puis récupère les groupes en analysant le fichier trié. Vous avez juste besoin de différents éléments, il vous suffit donc de hacher vos clés lors de l'analyse d'une seule table.
SquareCog

1
Ajoutez ORDER BY NULLà la GROUP BYversion et ils seront les mêmes.
Ariel

3

En MySQL , « Group By» utilise une étape supplémentaire: filesort. Je me rends compte que DISTINCTc'est plus rapide que GROUP BY, et c'était une surprise.


3

Après de lourds tests, nous sommes arrivés à la conclusion que GROUP BY est plus rapide

SELECT sql_no_cache opnamegroep_intern FROM telwerken WHERE opnemergroepIN (7,8,9,10,11,12,13) ​​group by opnamegroep_intern

635 totaal 0,0944 secondes Weergave van enregistre 0-29 (635 totaal, requête duurde 0,0484 sec)

SELECT sql_no_cache distinct (opnamegroep_intern) FROM telwerken WHERE opnemergroepIN (7,8,9,10,11,12,13)

635 totaal 0,2117 secondes (presque 100% plus lent) Weergave van enregistre 0-29 (635 totaal, requête duurde 0,3468 sec)


2

(plus d'une note fonctionnelle)

Il y a des cas où vous devez utiliser GROUP BY, par exemple si vous voulez obtenir le nombre d'employés par employeur:

SELECT u.employer, COUNT(u.id) AS "total employees" FROM users u GROUP BY u.employer

Dans un tel scénario DISTINCT u.employerne fonctionne pas correctement. Il y a peut-être un moyen, mais je ne le sais tout simplement pas. (Si quelqu'un sait comment faire une telle requête avec DISTINCT, veuillez ajouter une note!)


2

Voici une approche simple qui imprimera les 2 temps écoulés différents pour chaque requête.

DECLARE @t1 DATETIME;
DECLARE @t2 DATETIME;

SET @t1 = GETDATE();
SELECT DISTINCT u.profession FROM users u; --Query with DISTINCT
SET @t2 = GETDATE();
PRINT 'Elapsed time (ms): ' + CAST(DATEDIFF(millisecond, @t1, @t2) AS varchar);

SET @t1 = GETDATE();
SELECT u.profession FROM users u GROUP BY u.profession; --Query with GROUP BY
SET @t2 = GETDATE();
PRINT 'Elapsed time (ms): ' + CAST(DATEDIFF(millisecond, @t1, @t2) AS varchar);

OU essayez SET STATISTICS TIME (Transact-SQL)

SET STATISTICS TIME ON;
SELECT DISTINCT u.profession FROM users u; --Query with DISTINCT
SELECT u.profession FROM users u GROUP BY u.profession; --Query with GROUP BY
SET STATISTICS TIME OFF;

Il affiche simplement le nombre de millisecondes nécessaires pour analyser, compiler et exécuter chaque instruction comme ci-dessous:

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 2 ms.

1

Ce n'est pas une règle

Pour chaque requête .... essayez séparément séparément puis regroupez par ... comparez le temps pour terminer chaque requête et utilisez le plus rapidement ....

Dans mon projet, j'utilise parfois le groupe par et d'autres distincts


0

Si vous ne devez effectuer aucune fonction de groupe (somme, moyenne, etc. au cas où vous voudriez ajouter des données numériques à la table), utilisez SELECT DISTINCT. Je soupçonne que c'est plus rapide, mais je n'ai rien à prouver.

Dans tous les cas, si vous vous inquiétez de la vitesse, créez un index sur la colonne.


0

SELECT DISTINCT sera toujours le même, ou plus rapide, qu'un GROUP BY. Sur certains systèmes (par exemple Oracle), il peut être optimisé pour être identique à DISTINCT pour la plupart des requêtes. Sur d'autres (comme SQL Server), cela peut être considérablement plus rapide.


0

Si le problème le permet, essayez avec EXISTS, car il est optimisé pour se terminer dès qu'un résultat est trouvé (et ne pas tamponner de réponse), donc, si vous essayez simplement de normaliser les données pour une clause WHERE comme celle-ci

SELECT FROM SOMETHING S WHERE S.ID IN ( SELECT DISTINCT DCR.SOMETHING_ID FROM DIFF_CARDINALITY_RELATIONSHIP DCR ) -- to keep same cardinality

Une réponse plus rapide serait:

SELECT FROM SOMETHING S WHERE EXISTS ( SELECT 1 FROM DIFF_CARDINALITY_RELATIONSHIP DCR WHERE DCR.SOMETHING_ID = S.ID )

Ce n'est pas toujours possible mais une fois disponible, vous verrez une réponse plus rapide.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.