Cái gì nhanh hơn, CHỌN DISTINCT hoặc NHÓM THEO trong MySQL?


273

Nếu tôi có một cái bàn

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

và tôi muốn nhận tất cả các giá trị duy nhất của professiontrường, cái gì sẽ nhanh hơn (hoặc được khuyến nghị):

SELECT DISTINCT u.profession FROM users u

hoặc là

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

?


2
Bạn có thể tự kiểm tra nhanh như đặt câu hỏi. Thật khó chịu, gần như không thể xây dựng một kịch bản trong đó DISTINCT vượt trội hơn NHÓM THEO - điều này gây khó chịu vì rõ ràng đây không phải là mục đích của NHÓM THEO. Tuy nhiên, GROUP BY có thể tạo ra kết quả sai lệch, mà tôi nghĩ là đủ lý do để tránh nó.
Dâu

Có một bản sao khác với một câu trả lời khác. xem MySql - Phân biệt so với nhóm Bởi <<< nó nói NHÓM THEO tốt hơn
kolunar

Vui lòng xem tại đây nếu bạn muốn đo chênh lệch thời gian giữa DISTINCT và GROUP BY khi chạy truy vấn của bạn.
kolunar

Câu trả lời:


258

Chúng cơ bản tương đương với nhau (thực tế đây là cách một số cơ sở dữ liệu thực hiện DISTINCTdưới mui xe).

Nếu một trong số chúng nhanh hơn, nó sẽ thành DISTINCT. Điều này là do, mặc dù hai cái này giống nhau, một trình tối ưu hóa truy vấn sẽ phải nắm bắt thực tế là bạn GROUP BYkhông lợi dụng bất kỳ thành viên nào trong nhóm, chỉ là các khóa của chúng. DISTINCTlàm cho điều này rõ ràng, vì vậy bạn có thể thoát khỏi với một trình tối ưu hóa hơi bẩn.

Khi nghi ngờ, hãy kiểm tra!


76
DISTINCT sẽ nhanh hơn chỉ khi bạn KHÔNG có chỉ mục (vì nó không sắp xếp). Khi bạn có một chỉ mục và nó được sử dụng, chúng là từ đồng nghĩa.
Quassnoi

9
Định nghĩa DISTINCTGROUP BYkhác nhau ở chỗ DISTINCTkhông phải sắp xếp đầu ra và GROUP BYtheo mặc định. Tuy nhiên, trong MySQL ngay cả một DISTINCT+ ORDER BYcó thể vẫn được nhanh hơn GROUP BYdo sự gợi ý thêm cho tôi ưu hoa như được giải thích bởi SquareCog.
rustyx

1
DISTINCT nhanh hơn nhiều với dữ liệu số lượng lớn.
Pankaj Wanjari

7
Tôi đã thử nghiệm điều này và thấy rằng trên một cột được lập chỉ mục, mysql, nhóm của nó chậm hơn khoảng 6 lần so với phân biệt với một truy vấn khá phức tạp. Chỉ cần thêm điều này như một datapoint. Khoảng 100k hàng. Vì vậy, kiểm tra nó và xem cho chính mình.
Lizardx

xem MySql - Phân biệt so với nhóm Bởi <<< nó nói NHÓM THEO tốt hơn
kolunar

100

Nếu bạn có một chỉ mục trên profession, hai cái này là từ đồng nghĩa.

Nếu bạn không, sau đó sử dụng DISTINCT.

GROUP BYtrong MySQLcác loại kết quả. Bạn thậm chí có thể làm:

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

và nhận được ngành nghề của bạn được sắp xếp theo DESCthứ tự.

DISTINCTtạo một bảng tạm thời và sử dụng nó để lưu trữ các bản sao. GROUP BYkhông giống nhau, nhưng sắp xếp các kết quả khác biệt sau đó.

Vì thế

SELECT DISTINCT u.profession FROM users u

là nhanh hơn, nếu bạn không có một chỉ mục trên profession.


6
Bạn có thể thêm ORDER BY NULLvào GROUP BYđể tránh sắp xếp.
Ariel

Vẫn chậm hơn ngay cả khi nhóm bằng null
Thanh Trung

@ThanhTrung: cái gì chậm hơn cái gì?
Quassnoi

@Quassnoi nhóm chậm hơn so với khác biệt ngay cả khi tránh sắp xếp
Thanh Trung

Lưu ý: Vòng loại đặt hàng trên GROUP BY không được chấp nhận trong MySQL 8.
Matthew Lenz

18

Tất cả các câu trả lời ở trên là chính xác, đối với trường hợp DISTINCT trên một cột duy nhất so với NHÓM THEO trên một cột duy nhất. Mỗi công cụ db có triển khai và tối ưu hóa riêng và nếu bạn quan tâm đến sự khác biệt rất nhỏ (trong hầu hết các trường hợp) thì bạn phải kiểm tra máy chủ cụ thể VÀ phiên bản cụ thể! Vì việc triển khai có thể thay đổi ...

NHƯNG, nếu bạn chọn nhiều hơn một cột trong truy vấn, thì DISTINCT về cơ bản là khác nhau! Bởi vì trong trường hợp này, nó sẽ so sánh TẤT CẢ các cột của tất cả các hàng, thay vì chỉ một cột.

Vì vậy, nếu bạn có một cái gì đó như:

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

Đó là một lỗi phổ biến khi nghĩ rằng từ khóa DISTINCT phân biệt các hàng theo cột đầu tiên bạn đã chỉ định, nhưng DISTINCT là một từ khóa chung theo cách này.

Vì vậy, những người bạn phải cẩn thận không đưa ra các câu trả lời ở trên là chính xác cho tất cả các trường hợp ... Bạn có thể bị nhầm lẫn và nhận được kết quả sai trong khi tất cả những gì bạn muốn là tối ưu hóa!


3
Mặc dù câu hỏi này là về MySQL nhưng cần lưu ý rằng truy vấn thứ hai sẽ chỉ hoạt động trong MySQL. Gần như mọi DBMS khác sẽ từ chối câu lệnh thứ hai vì đó là việc sử dụng toán tử GROUP BY không hợp lệ.
a_horse_with_no_name

Chà, "gần như" là một định nghĩa có vấn đề :-) Sẽ hữu ích hơn nhiều nếu bạn nêu một DBMS cụ thể mà bạn đã kiểm tra để thấy rằng nó tạo ra lỗi cho câu lệnh này.
daniel.gindi

3
Postgres, Oracle, Firebird, DB2, SQL Server cho người mới bắt đầu. MySQL: sqlfiddle.com/#!2/6897c/1 Postgres: sqlfiddle.com/#!12/6897c/1 Oracle: sqlfiddle.com/#!12/6897c/1 Máy chủ SQL: sqlfiddle.com/#!6/ 6897c / 1
a_horse_with_no_name

17

Đi đơn giản và ngắn nhất nếu bạn có thể - DISTINCT dường như là nhiều hơn những gì bạn đang tìm kiếm chỉ vì nó sẽ cung cấp cho bạn chính xác câu trả lời bạn cần và chỉ có thế!


7

Nhóm bằng đắt hơn Phân biệt vì Nhóm bằng cách sắp xếp kết quả trong khi khác biệt tránh nó. Nhưng nếu bạn muốn tạo nhóm bằng cách mang lại kết quả tương tự như thứ tự phân biệt bằng null ..

SELECT DISTINCT u.profession FROM users u

bằng

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

bằng vớiSELECT profession FROM users GROUP BY profession

6

cũng khác biệt có thể chậm hơn so với nhóm trong một số trường hợp trong postgres (không biết về các dbs khác).

ví dụ thử nghiệm:

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

vì vậy hãy cẩn thận ... :)


5

Có vẻ như các truy vấn không hoàn toàn giống nhau. Ít nhất là cho MySQL.

Đối chiếu:

  1. mô tả chọn tên sản phẩm riêng biệt từ southwind.products
  2. mô tả chọn tên sản phẩm từ nhóm Northwind.products theo tên sản phẩm

Truy vấn thứ hai cung cấp thêm "Sử dụng tệp" trong Extra.


1
Họ giống nhau về những gì họ nhận được, không phải về cách họ nhận được nó. Một trình tối ưu hóa lý tưởng sẽ thực hiện chúng theo cùng một cách, nhưng trình tối ưu hóa MySQL không lý tưởng. Dựa trên bằng chứng của bạn, dường như DISTINCT sẽ đi nhanh hơn - O (n) so với O (n * log n).
SquareCog

Vì vậy, "sử dụng filesort" thực chất là điều xấu?
vava

Trong trường hợp này là bởi vì bạn không cần phải sắp xếp (bạn sẽ làm nếu bạn cần các nhóm). MySQL sắp xếp để đặt các mục giống nhau lại với nhau và sau đó nhận các nhóm bằng cách quét tệp đã sắp xếp. Bạn chỉ cần phân biệt, vì vậy bạn chỉ cần băm các phím của mình trong khi thực hiện quét một bảng.
SquareCog

1
Thêm ORDER BY NULLvào GROUP BYphiên bản và chúng sẽ giống nhau.
Ariel

3

Trong MySQL , " Group By" sử dụng một bước bổ sung : filesort. Tôi nhận ra DISTINCTlà nhanh hơn GROUP BY, và đó là một bất ngờ.


3

Sau khi thử nghiệm nặng, chúng tôi đã đi đến kết luận rằng NHÓM THEO nhanh hơn

CHỌN nhóm sql_no_cache opnamegroep_itern TỪ telwerken WHERE opnemergroepIN (7,8,9,10,11,12,13) ​​bởi opnamegroep_i INTERN

635 totaal 0,0944 giây Weergave van ghi 0 - 29 (635 totaal, truy vấn truy vấn 0,0484 giây)

CHỌN sql_no_cache khác biệt (opnamegroep_itern) TỪ telwerken WHERE opnemergroepIN (7,8,9,10,11,12,13)

635 totaal 0,2117 giây (chậm hơn gần 100%) Weergave van ghi 0 - 29 (635 totaal, truy vấn truy vấn 0,3468 giây)


2

(thêm một ghi chú chức năng)

Có những trường hợp khi bạn phải sử dụng GROUP BY, ví dụ nếu bạn muốn có được số lượng nhân viên trên mỗi nhà tuyển dụng:

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

Trong một kịch bản DISTINCT u.employernhư vậy không hoạt động đúng. Có lẽ có một cách, nhưng tôi không biết điều đó. (Nếu ai đó biết cách thực hiện một truy vấn như vậy với DISTINCT, vui lòng thêm một ghi chú!)


2

Đây là một cách tiếp cận đơn giản sẽ in 2 thời gian trôi qua khác nhau cho mỗi truy vấn.

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

HOẶC thử THỜI GIAN THỐNG KÊ (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;

Nó chỉ đơn giản hiển thị số mili giây cần thiết để phân tích, biên dịch và thực thi từng câu lệnh như dưới đây:

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

1

Đây không phải là một quy tắc

Đối với mỗi truy vấn .... hãy thử riêng biệt và sau đó nhóm bằng cách ... so sánh thời gian để hoàn thành mỗi truy vấn và sử dụng nhanh hơn ....

Trong dự án của tôi đôi khi tôi sử dụng nhóm bởi và những người khác


0

Nếu bạn không phải thực hiện bất kỳ chức năng nhóm nào (tổng, trung bình, v.v. trong trường hợp bạn muốn thêm dữ liệu số vào bảng), hãy sử dụng CHỌN DISTINCT. Tôi nghi ngờ nó nhanh hơn, nhưng tôi không có gì để hiển thị cho nó.

Trong mọi trường hợp, nếu bạn lo lắng về tốc độ, hãy tạo một chỉ mục trên cột.


0

CHỌN DISTINCT sẽ luôn giống nhau hoặc nhanh hơn NHÓM THEO. Trên một số hệ thống (ví dụ: Oracle), nó có thể được tối ưu hóa giống như DISTINCT cho hầu hết các truy vấn. Trên những người khác (như SQL Server), nó có thể nhanh hơn đáng kể.


0

Nếu sự cố cho phép, hãy thử với EXISTS, vì nó được tối ưu hóa để kết thúc ngay khi kết quả được tìm thấy (Và không đệm bất kỳ phản hồi nào), vì vậy, nếu bạn chỉ cố gắng bình thường hóa dữ liệu cho mệnh đề WHERE như thế này

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

Một phản ứng nhanh hơn sẽ là:

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

Điều này không phải lúc nào cũng có thể nhưng khi có sẵn bạn sẽ thấy phản hồi nhanh hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.