Cosa c'è di più veloce, SELEZIONA DISTINCT o GROUP BY in MySQL?


273

Se ho un tavolo

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)
)

e voglio ottenere tutti i valori univoci di professioncampo, cosa sarebbe più veloce (o raccomandato):

SELECT DISTINCT u.profession FROM users u

o

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

?


2
Potresti testare tu stesso rapidamente come porre la domanda. Irritante, è quasi impossibile costruire uno scenario in cui DISTINCT superi GRUPPO BY - il che è fastidioso perché chiaramente questo non è lo scopo di GROUP BY. Tuttavia, GROUP BY può produrre risultati fuorvianti, che ritengo sia una ragione sufficiente per evitarlo.
Fragola

C'è un altro duplicato con una risposta diversa. vedi MySql - Distinct vs Group By <<< dice che GROUP BY è meglio
kolunar

Vedere qui se si desidera misurare la differenza oraria tra DISTINCT e GROUP BY eseguendo la query.
Kolunar,

Risposte:


258

Sono sostanzialmente equivalenti tra loro (in effetti è così che alcuni database implementano DISTINCTsotto il cofano).

Se uno di questi è più veloce, lo sarà DISTINCT. Questo perché, sebbene i due siano gli stessi, un Query Optimizer dovrebbe cogliere il fatto che il tuo GROUP BYnon sta sfruttando alcun membro del gruppo, ma solo le loro chiavi. DISTINCTlo rende esplicito, così puoi farcela con un ottimizzatore leggermente più stupido.

In caso di dubbi, prova!


76
DISTINCT sarà più veloce solo se NON hai un indice (in quanto non si ordina). Quando hai un indice ed è usato, sono sinonimi.
Quassnoi,

10
La definizione di DISTINCTe GROUP BYdifferire in ciò DISTINCTnon deve ordinare l'output, e GROUP BYdi default lo fa. Tuttavia, in MySQL anche un DISTINCT+ ORDER BYpotrebbe essere ancora più veloce di un a GROUP BYcausa dei suggerimenti extra per l'ottimizzatore, come spiegato da SquareCog.
Rustyx,

1
DISTINCT è molto più veloce con grandi quantità di dati.
Pankaj Wanjari,

7
Ho provato questo, e ho scoperto che su una colonna indicizzata, mysql, group by era circa 6 volte più lento del distinto con una query abbastanza complicata. Aggiungo solo questo come punto dati. Circa 100.000 righe. Quindi prova e guarda tu stesso.
Lizardx,

vedi MySql - Distinct vs Group By <<< dice che GROUP BY è meglio
kolunar

100

Se hai un indice attivo profession, questi due sono sinonimi.

In caso contrario, utilizzare DISTINCT.

GROUP BYin una MySQLsorta di risultati. Puoi anche fare:

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

e DESCordina le tue professioni in ordine.

DISTINCTcrea una tabella temporanea e la utilizza per l'archiviazione dei duplicati. GROUP BYfa lo stesso, ma in seguito ordina i risultati distinti.

Così

SELECT DISTINCT u.profession FROM users u

è più veloce, se non hai un indice attivo profession.


6
È possibile aggiungere ORDER BY NULLa GROUP BYper evitare l'ordinamento.
Ariel,

Ancora più lento anche con il raggruppamento per null
Thanh Trung

@ThanhTrung: cosa c'è di più lento di cosa?
Quassnoi,

@Quassnoi groupby più lento che distinto anche se si evita l'ordinamento
Thanh Trung

Nota: le qualificazioni degli ordini su GROUP BY sono state deprecate in MySQL 8.
Matthew Lenz,

18

Tutte le risposte sopra sono corrette, per il caso di DISTINCT su una singola colonna vs GROUP BY su una singola colonna. Ogni motore db ha la sua implementazione e ottimizzazioni e, se ti preoccupi della minima differenza (nella maggior parte dei casi), devi testare su server specifici E versioni specifiche! Poiché le implementazioni possono cambiare ...

MA, se si seleziona più di una colonna nella query, DISTINCT è sostanzialmente diverso! Perché in questo caso confronterà TUTTE le colonne di tutte le righe, anziché solo una colonna.

Quindi se hai qualcosa come:

// 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

È un errore comune pensare che la parola chiave DISTINCT distingua le righe in base alla prima colonna specificata, ma DISTINCT è una parola chiave generica in questo modo.

Quindi le persone che devi fare attenzione a non prendere le risposte sopra come corrette per tutti i casi ... Potresti essere confuso e ottenere risultati sbagliati mentre tutto ciò che volevi era ottimizzare!


3
Anche se questa domanda è su MySQL si deve rilevare che la seconda query funzionerà solo in MySQL. Quasi ogni altro DBMS rifiuterà la seconda istruzione perché è un uso non valido dell'operatore GROUP BY.
a_horse_with_no_name

Bene, "quasi" è una definizione problematica :-) Sarebbe molto più utile se dichiari un DBMS specifico che hai testato per vedere che genera un errore per questa affermazione.
daniel.gindi,

3
Postgres, Oracle, Firebird, DB2, SQL Server per cominciare. 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

Scegli il più semplice e il più breve possibile: DISTINCT sembra essere più quello che stai cercando solo perché ti darà ESATTAMENTE la risposta di cui hai bisogno e solo quella!


7

Raggruppare per è costoso rispetto a Distinto poiché Raggruppa per fa una sorta sul risultato mentre distinto lo evita. Ma se si desidera creare un gruppo restituendo lo stesso risultato di un distinto, dare l' ordine per null ..

SELECT DISTINCT u.profession FROM users u

è uguale a

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

è uguale aSELECT profession FROM users GROUP BY profession

6

ben distinto può essere più lento del gruppo in alcune occasioni in postgres (non so di altri dbs).

esempio testato:

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

perciò stai attento ... :)


5

Sembra che le query non siano esattamente le stesse. Almeno per MySQL.

Confrontare:

  1. descrivere selezionare un nome prodotto distinto da northwind.products
  2. descrivere selezionare productname dal gruppo northwind.products per productname

La seconda query fornisce inoltre "Utilizzo di filesort" in Extra.


1
Sono gli stessi in termini di ciò che ottengono, non in termini di come lo ottengono. Un ottimizzatore ideale li eseguirà allo stesso modo, ma l'ottimizzatore MySQL non è l'ideale. Sulla base delle tue prove, sembrerebbe che DISTINCT andrebbe più veloce - O (n) vs O (n * log n).
SquareCog,

Quindi, "utilizzare filesort" è essenzialmente una cosa negativa?
vava,

In questo caso, perché non è necessario effettuare l'ordinamento (lo farei se avessi bisogno dei gruppi). MySQL ordina per mettere insieme le stesse voci e quindi ottenere gruppi scansionando il file ordinato. Hai solo bisogno di distinzioni, quindi devi solo eseguire l'hashing delle tue chiavi mentre esegui una singola scansione della tabella.
SquareCog,

1
Aggiungi ORDER BY NULLalla GROUP BYversione e saranno gli stessi.
Ariel,

3

In MySQL , " Group By" usa un passo in più: filesort. Mi rendo conto che DISTINCTè più veloce di GROUP BY, ed è stata una sorpresa.


3

Dopo pesanti test siamo giunti alla conclusione che GROUP BY è più veloce

SELEZIONA sql_no_cache opnamegroep_intern DA telwerken DOVE opnemergroepIN (7,8,9,10,11,12,13) ​​raggruppa per opnamegroep_intern

635 totaal 0,0944 secondi Weergave van records 0 - 29 (635 totaal, query duurde 0,0484 sec)

SELEZIONA sql_no_cache distinto (opnamegroep_intern) DA telwerken DOVE opnemergroepIN (7,8,9,10,11,12,13)

635 totali 0,2117 secondi (quasi il 100% più lenti) Registrazione furgoni Weergave 0-29 (635 totali, query duurde 0,3468 sec)


2

(più di una nota funzionale)

In alcuni casi è necessario utilizzare GROUP BY, ad esempio se si desidera ottenere il numero di dipendenti per datore di lavoro:

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

In un tale scenario DISTINCT u.employernon funziona bene. Forse c'è un modo, ma proprio non lo so. (Se qualcuno sa come effettuare una query del genere con DISTINCT, aggiungi una nota!)


2

Ecco un approccio semplice che stamperà i 2 diversi tempi trascorsi per ogni query.

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);

O prova 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;

Visualizza semplicemente il numero di millisecondi richiesti per analizzare, compilare ed eseguire ciascuna istruzione come di seguito:

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

1

Questa non è una regola

Per ogni query .... prova separatamente e poi raggruppa per ... confronta il tempo per completare ogni query e usa il più veloce ....

Nel mio progetto qualche volta uso group by e altri distinti


0

Se non è necessario eseguire alcuna funzione di gruppo (somma, media ecc. Nel caso si desideri aggiungere dati numerici alla tabella), utilizzare SELEZIONA DISTINCT. Ho il sospetto che sia più veloce, ma non ho nulla da mostrare per questo.

In ogni caso, se sei preoccupato per la velocità, crea un indice sulla colonna.


0

SELECT DISTINCT sarà sempre lo stesso o più veloce di un GROUP BY. Su alcuni sistemi (ad es. Oracle), potrebbe essere ottimizzato per essere uguale a DISTINCT per la maggior parte delle query. Su altri (come SQL Server), può essere notevolmente più veloce.


0

Se il problema lo consente, prova con EXISTS, poiché è ottimizzato per terminare non appena viene trovato un risultato (e non bufferizzare alcuna risposta), quindi, se stai solo cercando di normalizzare i dati per una clausola WHERE come questa

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

Una risposta più rapida sarebbe:

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

Questo non è sempre possibile ma quando disponibile vedrai una risposta più veloce.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.