MySQL เลือกหนึ่งคอลัมน์ DISTINCT พร้อมกับคอลัมน์อื่น ๆ ที่เกี่ยวข้อง


193
ID   FirstName   LastName
1      John        Doe
2      Bugs        Bunny
3      John        Johnson

ฉันต้องการที่จะเลือกDISTINCTผลที่ได้จากFirstNameคอลัมน์ แต่ฉันต้องการที่สอดคล้องกันและIDLastName

ชุดผลลัพธ์ต้องแสดงเพียงชุดเดียวJohnแต่มีID1 และ 1 LastNameของ Doe


1
คุณต้องการนามสกุลที่เป็นของ ID ต่ำสุดที่มีชื่อแตกต่างกันหรือไม่?
Thomas Langston

3
อะไรคือเหตุผลที่ควรเลือกสิ่งที่ดีที่สุด ฉันคิดว่าคุณต้องการให้ทั้ง John Doe และ John Johnson มาปรากฏตัวเนื่องจากเป็น Johns ที่แตกต่างกันสองตัว แต่นั่นเป็นเพียงฉัน
judda

4
DISTINCTไม่ใช่ฟังก์ชั่น คำตอบทั้งหมดด้วยDISTINCT()ผิด SELECTข้อผิดพลาดจะปรากฏขึ้นเมื่อคุณไม่ได้วางไว้หลังจากที่
คำถามมากเกิน

1
ALL คำตอบที่ใช้วงเล็บหลังจากคำที่แตกต่างแน่นอนผิด Distinct ไม่ใช่ฟังก์ชันดังนั้นจึงไม่สามารถยอมรับพารามิเตอร์ได้ วงเล็บที่แตกต่างกันดังต่อไปนี้จะถูกละเว้นเพียง ยกเว้นว่าคุณกำลังใช้ PostgreSQL โดยที่วงเล็บจะเป็น "ชนิดข้อมูลที่ซับซ้อน"
Used_By_Already

คำตอบ:


192

ลองใช้แบบสอบถามนี้

 SELECT ID, FirstName, LastName FROM table GROUP BY(FirstName)

16
เราจะรู้ได้อย่างไรว่าแถวไหนจะกลับมา?
William Entriken

27
@ เต็มคุณค่าคุณไม่สามารถทำได้ตามเอกสารของ MySQL : "เซิร์ฟเวอร์มีอิสระที่จะเลือกค่าใด ๆ จากแต่ละกลุ่มดังนั้นหากไม่เหมือนกันค่าที่เลือกจะไม่แน่นอน" ในทางปฏิบัติฉันใช้คิวรีชนิดนี้กับ ORDER BY clause ได้สำเร็จตัวอย่างเช่นคุณสามารถเพิ่ม ORDER BY id ASC / DESC และ MySQL จะให้ผลลัพธ์ที่สอดคล้องกันทุกครั้งที่คุณเรียกใช้คิวรี่ แต่ฉันจะแน่ใจว่าทุกคนควรใช้คุณสมบัติที่ไม่มีเอกสารในสภาพแวดล้อมการผลิตหรือไม่
Arunas Junevicius

2
OP ไม่ได้พูดถึงรุ่น mysql
diEcho

2
@sinaza ดูคำตอบที่อัปเดตของฉันสำหรับ MySQL 5.7.5+สำหรับการGROUP BYจัดการที่
2560

3
สิ่งนี้ใช้ไม่ได้กับโหมด only_full_group_by เนื่องจากไม่มี ID หรือ LastName ที่จะไม่ถูกรวมหรือเป็นส่วนหนึ่งของฟังก์ชั่นการจัดกลุ่ม ช่วยด้วย!
ihodonald

64

DISTINCTคำหลักไม่ได้จริงๆวิธีการทำงานที่คุณคาดหวังว่ามันจะ เมื่อคุณใช้SELECT DISTINCT col1, col2, col3คุณจะเลือก tuples {col1, col2, col3} ที่ไม่ซ้ำกันทั้งหมด


14
ขอบคุณสำหรับการชี้ไบรอันนี้ คุณสามารถแสดงตัวอย่างของวิธีที่ฉันสามารถใช้ GROUP BY เพื่อรับผลลัพธ์เดียวกันได้หรือไม่
นาย

59

เพื่อหลีกเลี่ยงผลลัพธ์ที่ไม่คาดคิดเมื่อใช้GROUP BYโดยไม่มีฟังก์ชันการรวมตามที่ใช้ในคำตอบที่ยอมรับเนื่องจาก MySQL มีอิสระในการดึงค่าใด ๆภายในชุดข้อมูลที่ถูกจัดกลุ่มเมื่อไม่ได้ใช้ฟังก์ชันรวม[sic]และปัญหาเกี่ยวกับONLY_FULL_GROUP_BYและปัญหาเกี่ยวกับโปรดลองใช้การยกเว้นการเข้าร่วม

การรวมเข้าร่วม - เอนทิตีที่ชัดเจน

สมมติว่าชื่อและนามสกุลเป็นดัชนีที่ไม่ซ้ำกัน(ไม่คลุมเครือ)อีกทางเลือกหนึ่งGROUP BYคือการจัดเรียงโดยใช้LEFT JOINเพื่อกรองชุดผลลัพธ์หรือที่เรียกว่าการยกเว้น JOIN

ดูการสาธิต

เรียงลำดับขึ้น(AZ)

หากต้องการดึงชื่อที่แตกต่างกันซึ่งเรียงลำดับตามนามสกุลจาก AZ

สอบถาม

SELECT t1.*
FROM table_name AS t1
LEFT JOIN table_name AS t2
ON t1.firstname = t2.firstname
AND t1.lastname > t2.lastname
WHERE t2.id IS NULL;

ผลลัพธ์

| id | firstname | lastname |
|----|-----------|----------|
|  2 |      Bugs |    Bunny |
|  1 |      John |      Doe |

ลำดับมากไปน้อย(ZA)

หากต้องการดึงข้อมูลชื่อที่แตกต่างซึ่งได้รับคำสั่งจากนามสกุลจาก ZA

สอบถาม

SELECT t1.*
FROM table_name AS t1
LEFT JOIN table_name AS t2
ON t1.firstname = t2.firstname
AND t1.lastname < t2.lastname
WHERE t2.id IS NULL;

ผลลัพธ์

| id | firstname | lastname |
|----|-----------|----------|
|  2 |      Bugs |    Bunny |
|  3 |      John |  Johnson |

จากนั้นคุณสามารถสั่งซื้อข้อมูลผลลัพธ์ตามต้องการ


การรวมเข้าร่วม - เอนทิตีที่ไม่ชัดเจน

หากการรวมกันของชื่อและนามสกุลไม่ซ้ำกัน(คลุมเครือ)และคุณมีค่าหลายแถวที่เหมือนกันคุณสามารถกรองชุดผลลัพธ์โดยรวมเงื่อนไข OR ในเกณฑ์ JOIN เพื่อกรองตามรหัสด้วย

ดูการสาธิต

ข้อมูล table_name

(1, 'John', 'Doe'),
(2, 'Bugs', 'Bunny'),
(3, 'John', 'Johnson'),
(4, 'John', 'Doe'),
(5, 'John', 'Johnson')

สอบถาม

SELECT t1.*
FROM table_name AS t1
LEFT JOIN table_name AS t2
ON t1.firstname = t2.firstname
AND (t1.lastname > t2.lastname
OR (t1.firstname = t1.firstname AND t1.lastname = t2.lastname AND t1.id > t2.id))
WHERE t2.id IS NULL;

ผลลัพธ์

| id | firstname | lastname |
|----|-----------|----------|
|  1 |      John |      Doe |
|  2 |      Bugs |    Bunny |

แบบสอบถามย่อยที่สั่ง

แก้ไข

คำตอบเดิมของฉันโดยใช้แบบสอบถามย่อยที่สั่งซื้อถูกเขียนก่อนMySQL 5.7.5ซึ่งไม่สามารถใช้งานได้อีกต่อไปเนื่องจากการเปลี่ยนแปลงด้วยONLY_FULL_GROUP_BYซึ่งใช้ไม่ได้แล้วเนื่องจากมีการเปลี่ยนแปลงด้วยโปรดใช้ตัวอย่างการเข้าร่วมการยกเว้นด้านบนแทน

นอกจากนี้ยังเป็นสิ่งสำคัญที่จะต้องทราบ; เมื่อONLY_FULL_GROUP_BYปิดการใช้งาน(พฤติกรรมดั้งเดิมก่อน MySQL 5.7.5)การใช้งานGROUP BYโดยไม่รวมฟังก์ชั่นอาจให้ผลลัพธ์ที่ไม่คาดคิดเพราะ MySQL มีอิสระที่จะเลือกค่าใด ๆภายในชุดข้อมูลที่ถูกจัดกลุ่ม[sic] [sic]

ความหมายIDหรือlastnameค่าอาจถูกดึงที่ไม่เกี่ยวข้องกับfirstnameแถวที่ดึงออกมา


คำเตือน

ด้วย MySQL GROUP BYอาจไม่ให้ผลลัพธ์ที่คาดหวังเมื่อใช้กับORDER BY

ดูตัวอย่างกรณีทดสอบ

วิธีที่ดีที่สุดของการนำไปใช้เพื่อให้มั่นใจถึงผลลัพธ์ที่คาดหวังคือการกรองขอบเขตชุดผลลัพธ์โดยใช้แบบสอบถามย่อยที่สั่งซื้อ

ข้อมูล table_name

(1, 'John', 'Doe'),
(2, 'Bugs', 'Bunny'),
(3, 'John', 'Johnson')

สอบถาม

SELECT * FROM (
    SELECT * FROM table_name ORDER BY ID DESC
) AS t1
GROUP BY FirstName

ผลลัพธ์

| ID | first |    last |
|----|-------|---------|
|  2 |  Bugs |   Bunny |
|  3 |  John | Johnson |

การเปรียบเทียบ

เพื่อแสดงผลลัพธ์ที่ไม่คาดคิดเมื่อใช้GROUP BYร่วมกับORDER BY

สอบถาม

SELECT * FROM table_name GROUP BY FirstName ORDER BY ID DESC

ผลลัพธ์

| ID | first |  last |
|----|-------|-------|
|  2 |  Bugs | Bunny |
|  1 |  John |   Doe |

3
คำตอบที่สมบูรณ์ที่สุดโดยไกล การเปลี่ยน 'ID desc' เป็น 'ID asc' ในการสืบค้นแรกทำให้เราสามารถเรียก 'John Doe' หรือ 'John Johnson' ได้ การเปลี่ยน 'ID desc' ในข้อความค้นหาที่สองไม่มีผลกระทบนี้
carla

ใน postgres คุณต้องมี ID ในกลุ่มโดยไม่แน่ใจว่า mysql
Sachin Prasad

กลุ่มตามคอลัมน์ -A เรียงตามคอลัมน์ -B ในหนึ่งคำสั่ง SELECT จะทำงานอย่างถูกต้องกับ MyriaDB รุ่นล่าสุดเสมอหรือไม่
Neal Davis

@NealDavis ตามMariaDBคู่มือOrdering is done after grouping.ดังนั้นไม่มีไม่ได้อยู่ในนี้กรณีการใช้งานนอกเหนือMariaDB ละเว้น ORDER BY ใน subqueries (ตามมาตรฐานของ SQL) LIMITโดยไม่ต้อง คุณต้องการใช้การWindow Functionชี้แจงเพิ่มเติมคุณควรถามคำถามของคุณในDBA stackexchangeเนื่องจากนี่เป็นคำถามที่เกี่ยวข้องกับ MySQL
fyrye

1
@NateS ไม่GROUP BYสามารถเลือกค่าใด ๆ ภายในชุดข้อมูลที่จัดกลุ่มเว้นแต่ฟังก์ชั่นรวมจะใช้ในคอลัมน์เหล่านั้นเพื่อบังคับค่าเฉพาะ ดังนั้นlastnameหรือidอาจมาจากแถวที่สั่งไว้ ตัวอย่างแบบสอบถามย่อยดั้งเดิมนั้นเป็นที่ยอมรับโดยค่าเริ่มต้นMySQL <= 5.7.4แต่ในทางเทคนิคยังคงมีปัญหาจากปัญหานี้ ในขณะที่ORDER BYช่วยป้องกันการเลือกแบบสุ่มมันก็ยังเป็นไปได้ในทางทฤษฎี แต่มีโอกาสน้อยกว่าอย่างมีนัยสำคัญกว่าโดยไม่ต้องใช้ORDER BYแบบสอบถามย่อย
fyrye




2

ไม่แน่ใจว่าคุณสามารถทำสิ่งนี้กับ MySQL ได้หรือไม่ แต่คุณสามารถใช้ CTE ใน T-SQL ได้

; WITH tmpPeople AS (
 SELECT 
   DISTINCT(FirstName),
   MIN(Id)      
 FROM People
)
SELECT
 tP.Id,
 tP.FirstName,
 P.LastName
FROM tmpPeople tP
JOIN People P ON tP.Id = P.Id

มิฉะนั้นคุณอาจต้องใช้ตารางชั่วคราว


1

ตามที่ระบุไว้โดยfyryeคำตอบที่ได้รับการยอมรับนั้นเกี่ยวข้องกับ MySQL รุ่นเก่าที่ONLY_FULL_GROUP_BYยังไม่ได้เปิดตัว ด้วย MySQL 8.0.17 (ใช้ในตัวอย่างนี้) เว้นแต่ว่าคุณจะปิดการใช้งานONLY_FULL_GROUP_BYคุณจะได้รับข้อความแสดงข้อผิดพลาดต่อไปนี้:

mysql> SELECT id, firstName, lastName FROM table_name GROUP BY firstName;

ข้อผิดพลาด 1,055 (42000): นิพจน์ # 1 ของรายการที่เลือกไม่ได้อยู่ในกลุ่มตามข้อและมีคอลัมน์ที่ไม่ได้รวม 'mydatabase.table_name.id' ซึ่งไม่ได้ขึ้นอยู่กับการใช้งานคอลัมน์ในกลุ่มตามข้อ; สิ่งนี้เข้ากันไม่ได้กับ sql_mode = only_full_group_by

วิธีหนึ่งในการทำงานรอบนี้ไม่ได้กล่าวถึงfyryeแต่อธิบายไว้ในhttps://dev.mysql.com/doc/refman/5.7/en/group-by-handling.htmlคือการใช้ANY_VALUE()ฟังก์ชั่นกับคอลัมน์ที่มี ไม่ได้อยู่ในGROUP BYข้อ ( idและlastNameในตัวอย่างนี้):

mysql> SELECT ANY_VALUE(id) as id, firstName, ANY_VALUE(lastName) as lastName FROM table_name GROUP BY firstName;
+----+-----------+----------+
| id | firstName | lastName |
+----+-----------+----------+
|  1 | John      | Doe      |
|  2 | Bugs      | Bunny    |
+----+-----------+----------+
2 rows in set (0.01 sec)

ตามที่เขียนไว้ในเอกสารดังกล่าว

ในกรณีนี้ MySQL จะเพิกเฉยต่อค่าที่อยู่ภายในของแต่ละกลุ่มชื่อและยอมรับการสืบค้น สิ่งนี้อาจมีประโยชน์หากคุณไม่สนใจว่าคอลัมน์ใดที่ไม่ได้รวบรวมจะถูกเลือกสำหรับแต่ละกลุ่ม ANY_VALUE()ไม่ได้เป็นฟังก์ชั่นรวมซึ่งแตกต่างจากฟังก์ชั่นเช่นหรือSUM() COUNT()มันทำหน้าที่ในการระงับการทดสอบสำหรับ nondeterminism


เพื่อความกระจ่างแจ้งฉันหลีกเลี่ยงเฉพาะการแนะนำให้ใช้ANY_VALUE()เป็นคำตอบและความคิดเห็นของฉันมุ่งเน้นไปที่การป้องกันชุดผลลัพธ์ที่ไม่ชัดเจนและไม่แน่นอน เนื่องจากเป็นชื่อฟังก์ชั่นที่แนะนำก็อาจส่งผลให้ค่าใด ๆ จากแถวที่เลือกจะถูกดึง ฉันอยากจะแนะนำให้ใช้MAXหรือMINแทน
fyrye

0

โปรดทราบเมื่อใช้กลุ่มโดยและสั่งซื้อโดยที่ MySQL เป็นฐานข้อมูลเท่านั้นที่ช่วยให้คอลัมน์ที่จะใช้ในกลุ่มโดยและ / หรือคำสั่งโดยชิ้นที่ไม่ได้เป็นส่วนหนึ่งของคำสั่งเลือก

ตัวอย่างเช่น: เลือก column1 จากกลุ่มตารางตามคำสั่ง column2 เรียงตามคอลัมน์ 3

ที่จะไม่บินในฐานข้อมูลอื่น ๆ เช่น Postgres, Oracle, MSSQL เป็นต้นคุณจะต้องทำสิ่งต่อไปนี้ในฐานข้อมูลเหล่านั้น

เลือกคอลัมน์ 1, คอลัมน์ 2, คอลัมน์ 3 จากกลุ่มตารางเรียงตามคอลัมน์ 2 เรียงลำดับตามคอลัมน์ 3

เพียงแค่ข้อมูลบางส่วนในกรณีที่คุณเคยโยกย้ายรหัสปัจจุบันของคุณไปยังฐานข้อมูลอื่นหรือเริ่มทำงานในฐานข้อมูลอื่นและลองใช้รหัสซ้ำ


-2

คุณสามารถใช้กลุ่มโดยเพื่อแสดงค่าที่แตกต่างและฟิลด์ที่เกี่ยวข้อง

select * from tabel_name group by FirstName

ตอนนี้คุณจะได้ผลลัพธ์ดังนี้:

ID    FirstName     LastName
2     Bugs          Bunny
1     John          Doe


หากคุณต้องการคำตอบเช่น

ID    FirstName     LastName
1     John          Doe
2     Bugs          Bunny

จากนั้นใช้แบบสอบถามนี้

select * from table_name group by FirstName order by ID

2
นี้จะไม่เสมอผลผลิตผลที่คาดหวังเมื่อจัดกลุ่มตามคำสั่งโดย
fyrye

-3
SELECT DISTINCT(firstName), ID, LastName from tableName GROUP BY firstName

จะเป็นทางออกที่ดีที่สุด IMO


32
สิ่งนี้จะไม่ทำงาน แต่จะใช้ ID และนามสกุลในการประเมินผลที่แตกต่างกัน
Ludo - ปิดบันทึก

2
ซึ่งเหมือนกับ DISTINCT (ชื่อ, ID, นามสกุล)
Tom Taylor

-4
SELECT DISTINCT (column1), column2
FROM table1
GROUP BY column1

1
DISTINCT()ไม่ใช่ฟังก์ชั่น DISTINCT และ GROUP BY กำลังทำสิ่งเดียวกันดังนั้นจึงไม่มีเหตุผลที่ทำให้พวกเขาทั้งคู่
Marki555

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