มีอะไรเร็วกว่าเลือก 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 - แตกต่าง VS กลุ่มโดย <<< มันบอกว่า GROUP BY จะดีกว่า
kolunar

โปรดดูที่นี่หากคุณต้องการวัดความแตกต่างของเวลาระหว่าง DISTINCT และ GROUP โดยเรียกใช้คิวรีของคุณ
kolunar

คำตอบ:


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 เร็วขึ้นมากด้วยข้อมูลจำนวนมาก
Pankaj Wanjari

7
ฉันทดสอบสิ่งนี้และพบว่าในคอลัมน์ที่จัดทำดัชนี mysql, กลุ่มโดยประมาณ 6x ช้ากว่าที่แตกต่างกันด้วยการค้นหาที่ค่อนข้างซับซ้อน เพียงเพิ่มสิ่งนี้เป็นดาต้าพอยท์ ประมาณ 100k แถว ดังนั้นลองทดสอบดูด้วยตัวคุณเอง
Lizardx

ดูMySql - แตกต่าง VS กลุ่มโดย <<< มันบอกว่า 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เพื่อหลีกเลี่ยงการเรียงลำดับ
Ariel

ยังคงช้าลงแม้จะมีการจัดกลุ่มตาม null
Thanh Trung

@ThanhTrung: อะไรช้ากว่าอะไร
Quassnoi

@Quassnoi groupby ช้ากว่าชัดเจนแม้ว่าจะหลีกเลี่ยงการเรียงลำดับ
Thanh Trung

หมายเหตุ: ตัวระบุคำสั่งซื้อใน GROUP BY ถูกเลิกใช้งานใน MySQL 8
Matthew Lenz

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 เกือบทุก DBMS อื่น ๆ จะปฏิเสธคำสั่งที่สองเพราะมันเป็นการใช้งานที่ไม่ถูกต้องของกลุ่มผู้ประกอบการ
a_horse_with_no_name

"เกือบ" เป็นคำจำกัดความที่เป็นปัญหา :-) มันจะมีประโยชน์มากขึ้นถ้าคุณระบุ DBMS เฉพาะที่คุณทดสอบเพื่อดูว่ามันสร้างข้อผิดพลาดสำหรับคำสั่งนี้
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: sqlfiddle.com/#!6/ 6897c / 1
a_horse_with_no_name

17

ไปหาสิ่งที่ง่ายและสั้นที่สุดถ้าทำได้ - ความแตกต่างนั้นดูเหมือนจะเป็นสิ่งที่คุณกำลังมองหามากขึ้นเพราะมันจะให้คำตอบที่คุณต้องการและสิ่งนั้น!


7

จัดกลุ่มตามราคาแพงกว่า Distinct เนื่องจากจัดกลุ่มตามผลลัพธ์ในขณะที่หลีกเลี่ยงที่แตกต่างกัน แต่ถ้าคุณต้องการที่จะทำให้กลุ่มโดยให้ผลเช่นเดียวกับที่แตกต่างกันให้สั่งโดย null ..

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) vs O (n * log n)
SquareCog

ดังนั้น "การใช้ filesort" จึงเป็นสิ่งที่เลวร้าย?
vava

ในกรณีนี้เป็นเพราะคุณไม่จำเป็นต้องเรียงลำดับ (คุณต้องการหากคุณต้องการกลุ่ม) MySQL เรียงลำดับเพื่อรวมรายการเดียวกันเข้าด้วยกันแล้วรับกลุ่มโดยสแกนไฟล์ที่เรียงลำดับ คุณต้องการความแตกต่างดังนั้นคุณเพียงแค่ต้องแฮชคีย์ของคุณในขณะที่ทำการสแกนตารางเดี่ยว
SquareCog

1
เพิ่มORDER BY NULLไปยังGROUP BYรุ่นและพวกเขาจะเหมือนกัน
Ariel

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 totaal 0.0944 วินาที Weergave van บันทึก 0 - 29 (635 totaal, query duurde 0.0484 วินาที)

SELECT sql_no_cache ที่แตกต่างกัน (opnamegroep_intern) จากtelwerken WHERE opnemergroepIN (7,8,9,10,11,12,13)

635 totaal 0.2117 วินาที (ช้ากว่าเกือบ 100%) Weergave van บันทึก 0 - 29 (635 totaal, query 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

เลือก 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 )

สิ่งนี้ไม่สามารถทำได้เสมอไป แต่ถ้ามีคุณจะเห็นคำตอบที่รวดเร็ว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.