Что быстрее, SELECT DISTINCT или GROUP BY в MySQL?


273

Если у меня есть стол

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

и я хочу получить все уникальные значения professionполя, что будет быстрее (или рекомендуется):

SELECT DISTINCT u.profession FROM users u

или

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

?


2
Вы можете проверить себя так же быстро, как и задать вопрос. Раздражает то, что почти невозможно построить сценарий, в котором DISTINCT превосходит GROUP BY - что раздражает, потому что, очевидно, это не является целью GROUP BY. Однако GROUP BY может привести к вводящим в заблуждение результатам, что, я думаю, является достаточной причиной, чтобы этого избежать.
Клубника

Есть еще один дубликат с другим ответом. см. MySql - Отдельно от группы К <<< говорится, что GROUP BY лучше
kolunar

Пожалуйста, посмотрите здесь, если вы хотите измерить разницу во времени между запросами DISTINCT и GROUP BY.
Колунар

Ответы:


258

Они по существу эквивалентны друг другу (фактически, это то, как некоторые базы данных реализуются DISTINCTпод капотом).

Если один из них быстрее, он будет DISTINCT. Это связано с тем, что, хотя они одинаковы, оптимизатору запросов придется уловить тот факт, что вы GROUP BYне пользуетесь никакими членами группы, а только их ключами. DISTINCTделает это явным, так что вы можете обойтись немного более тупым оптимизатором.

Если есть сомнения, проверьте!


76
DISTINCT будет быстрее, только если у вас нет индекса (так как он не сортируется). Когда у вас есть индекс, и он используется, это синонимы.
Quassnoi

10
Определение DISTINCTи GROUP BYотличается тем, что DISTINCTне нужно сортировать вывод, и GROUP BYпо умолчанию делает. Однако, в MySQL даже DISTINCT+ ORDER BYможет еще быстрее , чем GROUP BYиз - за дополнительные подсказки для оптимизатора , как объяснена SquareCog.
rustyx

1
DISTINCT намного быстрее с большими объемами данных.
Панкадж Ванджари

7
Я проверил это и обнаружил, что в индексируемом столбце mysql group by был примерно в 6 раз медленнее, чем отдельный с довольно сложным запросом. Просто добавив это как точку данных. Около 100 тысяч строк. Так что проверьте это и убедитесь сами.
Lizardx

см. MySql - Отличный от группы. По <<< говорится, что GROUP BY лучше
kolunar

100

Если у вас есть индекс profession, эти два являются синонимами.

Если нет, то используйте DISTINCT.

GROUP BYв MySQLсортировке результатов. Вы даже можете сделать:

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

и приведите свои профессии в DESCпорядок.

DISTINCTсоздает временную таблицу и использует ее для хранения дубликатов. GROUP BYделает то же самое, но сортирует отличные результаты впоследствии.

Так

SELECT DISTINCT u.profession FROM users u

быстрее, если у вас нет индекса на profession.


6
Вы можете добавить ORDER BY NULLк, GROUP BYчтобы избежать сортировки.
Ариэль

Еще медленнее даже с группировкой по нулю
Тхан Чунг

@ThanhTrung: что медленнее чем?
Quassnoi

@Quassnoi groupby медленнее, чем отчетливый, даже если избегать сортировки
Thanh Trung

Примечание. В MySQL 8 классификаторы заказов на GROUP BY устарели.
Мэтью Ленц

18

Все ответы выше верны, для случая DISTINCT в одном столбце против GROUP BY в одном столбце. Каждый механизм БД имеет свою собственную реализацию и оптимизацию, и если вы заботитесь об очень небольшой разнице (в большинстве случаев), то вам придется тестировать на конкретном сервере И на конкретной версии! Как реализации могут измениться ...

НО, если вы выберете более одного столбца в запросе, то DISTINCT существенно отличается! Потому что в этом случае он будет сравнивать ВСЕ столбцы всех строк, а не только один столбец.

Так что если у вас есть что-то вроде:

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

Распространенной ошибкой считается, что ключевое слово DISTINCT различает строки по первому указанному вами столбцу, но DISTINCT является общим ключевым словом в этом смысле.

Таким образом, люди, с которыми вы должны быть осторожны, чтобы не принимать приведенные выше ответы как правильные для всех случаев ... Вы можете запутаться и получить неправильные результаты, в то время как все, что вам нужно, это оптимизировать!


3
Хотя этот вопрос является о MySQL следует отметить , что второй запрос будет работать только в MySQL. Почти каждая другая СУБД будет отклонять второй оператор, потому что это недопустимое использование оператора GROUP BY.
a_horse_with_no_name

Что ж, «почти» - это проблематичное определение :-) Было бы гораздо полезнее, если бы вы указали конкретную СУБД, которую вы тестировали, чтобы убедиться, что она генерирует ошибку для этого оператора.
daniel.gindi

3
Postgres, Oracle, Firebird, DB2, SQL Server для начинающих. 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

Сделайте самое простое и самое короткое, если можете - DISTINCT, кажется, больше того, что вы ищете, только потому, что он даст вам именно тот ответ, который вам нужен, и только это!


7

Группировка by обходится дороже чем Distinct, так как Group by делает сортировку по результату, а отдельный избегает его. Но если вы хотите сделать группировку по результату, дайте тот же результат, что и отдельный, задайте порядок по нулю .

SELECT DISTINCT u.profession FROM users u

равно

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

равноSELECT profession FROM users GROUP BY profession

6

В некоторых случаях в Postgres хорошо различимый может быть медленнее, чем групповой (не знаю о других БД).

проверенный пример:

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

так что будьте осторожны ... :)


5

Кажется, что запросы не совсем одинаковы. По крайней мере, для MySQL.

Для сравнения:

  1. опишите выбрать отличное название продукта от northwind.products
  2. описать выбрать название продукта из группы northwind.products по названию продукта

Второй запрос дает дополнительно «Использование файловой сортировки» в Extra.


1
Они одинаковы с точки зрения того, что они получают, а не с точки зрения того, как они получают это. Идеальный оптимизатор будет выполнять их таким же образом, но оптимизатор MySQL не идеален. На основании ваших доказательств может показаться, что DISTINCT будет работать быстрее - O (n) против O (n * log n).
SquareCog

Таким образом, "использование файловой сортировки" по сути плохо?
Вава

В этом случае это так, потому что вам не нужно сортировать (вы бы, если вам нужны группы). MySQL сортирует, чтобы поместить те же самые записи вместе, и затем получить группы, просматривая отсортированный файл. Вам просто нужны различия, поэтому вам нужно просто хэшировать свои ключи при выполнении сканирования одной таблицы.
SquareCog

1
Добавьте ORDER BY NULLк GROUP BYверсии и они будут одинаковыми.
Ариэль

3

В MySQL , « Group By» использует дополнительный шаг: filesort. Я понимаю DISTINCT, быстрее GROUP BY, и это было неожиданно.


3

После тяжелых испытаний мы пришли к выводу, что GROUP BY быстрее

ВЫБЕРИТЕ sql_no_cache opnamegroep_intern ОТ telwerken ГДЕ opnemergroepВ (7,8,9,10,11,12,13) ​​группировать по opnamegroep_intern

635 итого 0,0944 секунды Weergave van записывает 0 - 29 (635 итога, запрос duurde 0,0484 сек)

ВЫБЕРИТЕ sql_no_cache отчетливый (opnamegroep_intern) ОТ telwerken ГДЕ opnemergroep(7,8,9,10,11,12,13)

635 суммарных 0,2117 секунд (почти на 100% медленнее) Weergave van записывает 0 - 29 (635 суммарных, запрос duurde 0,3468 секунд)


2

(больше функциональной ноты)

Есть случаи, когда вам нужно использовать GROUP BY, например, если вы хотите получить количество сотрудников на одного работодателя:

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

В таком случае DISTINCT u.employerне работает правильно. Возможно, есть способ, но я просто не знаю его. (Если кто-то знает, как сделать такой запрос с помощью DISTINCT, добавьте примечание!)


2

Вот простой подход, который будет печатать 2 разных прошедших времени для каждого запроса.

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

ИЛИ попробуйте УСТАНОВИТЬ ВРЕМЯ СТАТИСТИКИ (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;

Он просто отображает количество миллисекунд, необходимое для анализа, компиляции и выполнения каждого оператора, как показано ниже:

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

1

Это не правило

Для каждого запроса .... попробуйте отдельно различаться, а затем сгруппировать по ... сравните время выполнения каждого запроса и используйте более быстрый ....

В моем проекте иногда я использую группу, а другие


0

Если вам не нужно выполнять какие-либо групповые функции (сумма, среднее и т. Д., Если вы хотите добавить числовые данные в таблицу), используйте SELECT DISTINCT. Я подозреваю, что это быстрее, но мне нечего показать.

В любом случае, если вас беспокоит скорость, создайте индекс для столбца.


0

SELECT DISTINCT всегда будет таким же или быстрее, чем GROUP BY. В некоторых системах (например, Oracle) он может быть оптимизирован, чтобы быть таким же, как DISTINCT для большинства запросов. На других (таких как SQL Server) это может быть значительно быстрее.


0

Если проблема позволяет, попробуйте использовать EXISTS, так как он оптимизирован для завершения, как только будет найден результат (и не буферизует какой-либо ответ), поэтому, если вы просто пытаетесь нормализовать данные для предложения WHERE, подобного этому

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

Более быстрый ответ будет:

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

Это не всегда возможно, но когда доступно, вы увидите более быстрый ответ.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.