O que é mais rápido, SELECT DISTINCT ou GROUP BY no MySQL?


273

Se eu tiver uma mesa

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 quero obter todos os valores exclusivos de profession campo, o que seria mais rápido (ou recomendado):

SELECT DISTINCT u.profession FROM users u

ou

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

?


2
Você pode testar por si mesmo o mais rápido possível. Irritantemente, é quase impossível construir um cenário em que DISTINCT supera o GROUP BY - o que é irritante, porque claramente esse não é o objetivo do GROUP BY. No entanto, o GROUP BY pode produzir resultados enganosos, o que eu acho que é motivo suficiente para evitá-lo.
Morango

Há outra duplicata com uma resposta diferente. veja MySql - Distinto vs Group By <<< diz GROUP BY é melhor
kolunar

Por favor, veja aqui se deseja medir a diferença horária entre DISTINCT e GROUP, executando sua consulta.
Kolunar

Respostas:


258

Eles são essencialmente equivalentes entre si (na verdade, é assim que alguns bancos de dados implementam DISTINCT sob o capô).

Se um deles for mais rápido, será DISTINCT. Isso ocorre porque, embora os dois sejam iguais, um otimizador de consulta precisaria entender o fato de que seuGROUP BY não está aproveitando nenhum membro do grupo, apenas as chaves deles.DISTINCTtorna isso explícito, para que você possa se safar com um otimizador um pouco mais burro.

Em caso de dúvida, teste!


76
DISTINCT será mais rápido apenas se você não tiver um índice (pois não classifica). Quando você tem um índice e é usado, eles são sinônimos.
Quassnoi 27/02/09

10
A definição DISTINCTe a GROUP BYdiferença disso DISTINCTnão precisam classificar a saída e, GROUP BYpor padrão, sim. No entanto, no MySQL, mesmo um DISTINCT+ aindaORDER BY pode ser mais rápido que um, devido às dicas extras para o otimizador, conforme explicado pelo SquareCog. GROUP BY
Rustyx

1
DISTINCT é muito mais rápido com grande quantidade de dados.
Pankaj Wanjari

7
Testei isso e descobri que em uma coluna indexada, o mysql, o grupo by era cerca de 6x mais lento que o distinto, com uma consulta bastante complicada. Apenas adicionando isso como um ponto de dados. Cerca de 100 mil linhas. Portanto, teste e veja por si mesmos.
Lizardx

veja MySql - Distinto vs Group By <<< diz GROUP BY é melhor
kolunar

100

Se você tem um índice em profession , esses dois são sinônimos.

Caso contrário, use DISTINCT.

GROUP BYem MySQLclassifica os resultados. Você pode até fazer:

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

e organize suas profissões em DESCordem.

DISTINCT cria uma tabela temporária e a usa para armazenar duplicatas. GROUP BYfaz o mesmo, mas classifica os resultados distintos posteriormente.

assim

SELECT DISTINCT u.profession FROM users u

é mais rápido, se você não tiver um índice profession.


6
Você pode adicionar ORDER BY NULLao GROUP BYpara evitar a classificação.
Ariel #

Ainda mais lento, mesmo com agrupamento por null
Thanh Trung

@ ThanhTrung: o que é mais lento do que o que?
Quassnoi 07/06/19

@Quassnoi groupby mais lento do que distinta mesmo evitando tipo
Thanh Trung

Nota: Os qualificadores de pedidos no GROUP BY foram preteridos no MySQL 8.
Matthew Lenz

18

Todas as respostas acima estão corretas, no caso de DISTINCT em uma única coluna vs GROUP BY em uma única coluna. Todo mecanismo db tem sua própria implementação e otimizações, e se você se preocupa com a pouca diferença (na maioria dos casos), precisa testar contra um servidor específico E uma versão específica! Como as implementações podem mudar ...

MAS, se você selecionar mais de uma coluna na consulta, o DISTINCT será essencialmente diferente! Porque neste caso, ele comparará TODAS as colunas de todas as linhas, em vez de apenas uma coluna.

Então, se você tem algo como:

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

É um erro comum pensar que a palavra-chave DISTINCT distingue linhas pela primeira coluna especificada, mas a DISTINCT é uma palavra-chave geral dessa maneira.

Portanto, as pessoas precisam ter cuidado para não aceitar as respostas acima como corretas para todos os casos ... Você pode ficar confuso e obter resultados errados enquanto tudo o que queria era otimizar!


3
Embora esta pergunta seja sobre o MySQL, deve-se notar que a segunda consulta funcionará apenas no MySQL. Quase todos os outros DBMS rejeitam a segunda instrução porque é um uso inválido do operador GROUP BY.
A_horse_with_no_name 15/09

Bem, "quase" é uma definição problemática :-) Seria muito mais útil se você declarar um DBMS específico que você testou para ver se ele gera um erro para esta declaração.
Daniel.gindi 15/09/2013

3
Postgres, Oracle, Firebird, DB2, SQL Server para iniciantes. 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 15/09

17

Escolha o mais simples e o mais curto possível - DISTINCT parece ser mais o que você está procurando, apenas porque lhe dará EXATAMENTE a resposta que você precisa e somente isso!


7

Agrupar por é mais caro que Distinto, pois Agrupar faz uma classificação no resultado, enquanto distinto o evita. Mas se você deseja criar grupo, produza o mesmo resultado que distinto, dê ordem por nulo .

SELECT DISTINCT u.profession FROM users u

é igual a

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

é igual aSELECT profession FROM users GROUP BY profession

6

bem distinto pode ser mais lento que o grupo em algumas ocasiões no postgres (não conheço outros dbs).

exemplo testado:

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

por isso tem cuidado ... :)


5

Parece que as consultas não são exatamente as mesmas. Pelo menos para o MySQL.

Comparar:

  1. descreva o nome do produto distinto e selecione northwind.products
  2. descrever o nome do produto selecionado no grupo northwind.products por nome do produto

A segunda consulta fornece adicionalmente "Using filesort" no Extra.


1
Eles são os mesmos em termos do que obtêm, não em termos de como obtêm. Um otimizador ideal os executaria da mesma maneira, mas o otimizador do MySQL não é o ideal. Com base nas suas evidências, parece que DISTINCT iria mais rápido - O (n) vs O (n * log n).
249 SquareCog

Então, "usar filesort" é essencialmente uma coisa ruim?
vava

Nesse caso, é porque você não precisa classificar (você faria se precisasse dos grupos). O MySQL classifica para reunir as mesmas entradas e, em seguida, obter grupos examinando o arquivo classificado. Você só precisa de distinções, portanto, basta digitar suas chaves enquanto faz uma única verificação de tabela.
249 SquareCog

1
Adicione ORDER BY NULLà GROUP BYversão e eles serão os mesmos.
Ariel

3

No MySQL " Group By" usa um passo extra: filesort. Percebo que DISTINCTé mais rápido do que GROUP BY, e isso foi uma surpresa.


3

Após testes pesados, chegamos à conclusão de que o GROUP BY é mais rápido

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

635 total 0,0944 segundos Registros de van Weergave 0 - 29 (635 total, consulta dupla 0,0484 s)

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

635 total 0,2117 segundos (quase 100% mais lento) Registros de van 0 - 29 (635 total, consulta dupla 0,3468 s)


2

(mais uma nota funcional)

Há casos em que você precisa usar o GROUP BY, por exemplo, se deseja obter o número de funcionários por empregador:

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

Nesse cenário DISTINCT u.employer, não funciona direito. Talvez exista um caminho, mas eu simplesmente não o conheço. (Se alguém souber fazer essa consulta com DISTINCT, adicione uma observação!)


2

Aqui está uma abordagem simples que imprimirá os 2 tempos decorridos diferentes para cada consulta.

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

Ele simplesmente exibe o número de milissegundos necessário para analisar, compilar e executar cada instrução como abaixo:

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

1

Isto não é uma regra

Para cada consulta .... tente separadamente e agrupe por ... compare o tempo para concluir cada consulta e use o mais rápido ....

Em meu projeto, em algum momento, uso grupo por e outros distintos


0

Se você não precisar executar nenhuma função de grupo (soma, média, etc, caso deseje adicionar dados numéricos à tabela), use SELECT DISTINCT. Eu suspeito que seja mais rápido, mas não tenho nada para mostrar.

De qualquer forma, se você estiver preocupado com a velocidade, crie um índice na coluna.


0

SELECT DISTINCT será sempre o mesmo ou mais rápido que um GROUP BY. Em alguns sistemas (por exemplo, Oracle), pode ser otimizado para ser o mesmo que DISTINCT na maioria das consultas. Em outros (como o SQL Server), pode ser consideravelmente mais rápido.


0

Se o problema permitir, tente com EXISTS, pois ele é otimizado para terminar assim que um resultado é encontrado (e não armazena nenhuma resposta); portanto, se você está apenas tentando normalizar dados para uma cláusula WHERE como esta

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

Uma resposta mais rápida seria:

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

Isso nem sempre é possível, mas quando disponível, você verá uma resposta mais rápida.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.