Що швидше, 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 краще
koluнар

Будь ласка, дивіться тут, якщо ви хочете виміряти різницю в часі між DISTINCT та GROUP за допомогою запиту.
коленар

Відповіді:


258

Вони по суті є рівнозначними один одному (адже саме так реалізуються деякі бази даних) DISTINCT під кришкою).

Якщо одна з них швидша, це буде DISTINCT. Це тому, що, хоча вони два однакові, оптимізатору запитів доведеться зафіксувати той факт, що ваш користувач GROUP BYне користується жодними членами групи, а лише їх ключами.DISTINCTробить це явним, тому ви можете піти з трохи тупішим оптимізатором.

Коли ви сумніваєтесь, тестуйте!


76
DISTINCT буде швидше, лише якщо у вас НЕ буде індекс (як він не сортує). Коли у вас є індекс і він використовується, вони є синонімами.
Quassnoi

10
Визначення DISTINCTта GROUP BYвідмінність у цьому DISTINCTне має сортувати вихід, а GROUP BYза замовчуванням робить. Однак у MySQL навіть DISTINCT+ ORDER BYможе все-таки бути швидшим, ніж GROUP BYчерез додаткові підказки для оптимізатора, як пояснив SquareCog.
іржа

1
DISTINCT набагато швидше з великою кількістю даних.
Pankaj Wanjari

7
Я перевірив це, і виявив, що в індексованому стовпці, mysql, група по групі була приблизно в 6 разів повільніше, ніж різниця з досить складним запитом. Просто додайте це як точку даних. Близько 100k рядків. Тож протестуйте і переконайтеся самі.
Лізард

дивіться MySql - Відмінна проти групи. <<< в ній сказано, що GROUP BY краще
koluнар

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щоб уникнути сортування.
Аріель

Ще повільніше, навіть з групуванням за нулем
Thanh Trung

@ThanhTrung: що повільніше, ніж що?
Quassnoi

@Quassnoi групою повільніше, ніж чітко, навіть якщо уникнути сортування
Thanh Trung

Примітка. Класифікатори замовлень на групі BY були застаріли в MySQL 8.
Меттью Ленц

18

Усі відповіді, наведені вище, є правильними, для випадку DISTINCT в одному стовпчику проти GROUP BY в одному стовпчику. Кожен db-движок має власну реалізацію та оптимізацію, і якщо ви дбаєте про дуже малу різницю (в більшості випадків), то вам доведеться протестувати на конкретному сервері І конкретній версії! Оскільки реалізації можуть змінюватися ...

АЛЕ, якщо у запиті ви обрали більше одного стовпця, то 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

Група по дорого, ніж розрізнена, оскільки 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 (не знайте про інші dbs).

перевірений приклад:

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 за назвою продукту

Другий запит додатково дає "Використання файлового ряду" в додатковому.


1
Вони однакові з точки зору того, що отримують, а не з точки зору того, як вони його отримують. Ідеальний оптимізатор виконав би їх так само, але оптимізатор MySQL не є ідеальним. Виходячи з ваших доказів, здавалося б, що DISTINCT піде швидше - O (n) проти O (n * log n).
SquareCog

Отже, "використання файлів" - це по суті погана річ?
vava

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

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

3

В MySQL , « Group By» використовує додатковий крок: filesort. Я розумію, DISTINCTце швидше GROUP BY, і це було несподіванкою.


3

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

SELECT SQL_NO_CACHE opnamegroep_intern ВІД telwerken WHERE opnemergroepIN (7,8,9,10,11,12,13) групи по opnamegroep_intern

635 загальний 0,0944 секунди Веергавський рекорд 0 - 29 (635 загальний, запит дурде 0,0484 сек)

SELECT SQL_NO_CACHE різні (opnamegroep_intern) ВІД telwerken КУДИ opnemergroepВ (7,8,9,10,11,12,13)

635 загальний 0,2117 секунди (майже на 100% повільніше) Веергайв записує 0 - 29 (635 сукупно, запит дурде 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

ВИБІР ВИДАЛЕННЯ завжди буде однаковим або швидшим, ніж ГРУПА ПО. У деяких системах (наприклад, 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.