คุณต้องการได้แถวที่มีสูงสุดOrderField
ต่อกลุ่มหรือไม่? ฉันจะทำแบบนี้:
SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId AND t1.OrderField < t2.OrderField
WHERE t2.GroupId IS NULL
ORDER BY t1.OrderField; // not needed! (note by Tomas)
( แก้ไขโดย Tomas:หากมีเรกคอร์ดที่มี OrderField เดียวกันภายในกลุ่มเดียวกันมากขึ้นและคุณต้องการหนึ่งในนั้นคุณอาจต้องการขยายเงื่อนไข:
SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId
AND (t1.OrderField < t2.OrderField
OR (t1.OrderField = t2.OrderField AND t1.Id < t2.Id))
WHERE t2.GroupId IS NULL
สิ้นสุดการแก้ไข)
ในคำอื่น ๆ กลับแถวt1
ที่ไม่มีแถวอื่น ๆt2
ที่มีอยู่ด้วยกันและมากขึ้นGroupId
OrderField
เมื่อt2.*
เป็นโมฆะหมายความว่าการรวมภายนอกด้านซ้ายไม่พบการจับคู่ดังกล่าวดังนั้นจึงt1
มีค่ามากที่สุดOrderField
ในกลุ่ม
ไม่มีอันดับไม่มีแบบสอบถามย่อย นี้ควรทำงานได้อย่างรวดเร็วและเพิ่มประสิทธิภาพการเข้าถึงการ T2 กับ "การใช้ดัชนี" (GroupId, OrderField)
ถ้าคุณมีดัชนีสารประกอบบน
เกี่ยวกับประสิทธิภาพให้ดูคำตอบของฉันที่จะดึงข้อมูลระเบียนสุดท้ายในแต่ละกลุ่ม ฉันลองใช้วิธีการสืบค้นย่อยและวิธีการเข้าร่วมโดยใช้การถ่ายโอนข้อมูล Stack Overflow ความแตกต่างที่น่าทึ่งคือวิธีการเข้าร่วมทำงานได้เร็วขึ้น 278 เท่าในการทดสอบของฉัน
สิ่งสำคัญคือคุณต้องมีดัชนีที่ถูกต้องเพื่อให้ได้ผลลัพธ์ที่ดีที่สุด!
เกี่ยวกับวิธีการของคุณโดยใช้ตัวแปร @Rank มันจะไม่ทำงานตามที่คุณเขียนไว้เนื่องจากค่าของ @Rank จะไม่รีเซ็ตเป็นศูนย์หลังจากที่แบบสอบถามประมวลผลตารางแรกแล้ว ฉันจะแสดงตัวอย่าง
ฉันแทรกข้อมูลจำลองโดยมีฟิลด์พิเศษที่เป็นโมฆะยกเว้นในแถวที่เรารู้ว่ามีค่ามากที่สุดต่อกลุ่ม:
select * from `Table`;
+
| GroupId | OrderField | foo |
+
| 10 | 10 | NULL |
| 10 | 20 | NULL |
| 10 | 30 | foo |
| 20 | 40 | NULL |
| 20 | 50 | NULL |
| 20 | 60 | foo |
+
เราสามารถแสดงให้เห็นว่าอันดับเพิ่มขึ้นเป็นสามสำหรับกลุ่มแรกและหกสำหรับกลุ่มที่สองและเคียวรีภายในจะส่งกลับสิ่งเหล่านี้อย่างถูกต้อง:
select GroupId, max(Rank) AS MaxRank
from (
select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField) as t
group by GroupId
+
| GroupId | MaxRank |
+
| 10 | 3 |
| 20 | 6 |
+
ตอนนี้เรียกใช้แบบสอบถามโดยไม่มีเงื่อนไขการรวมเพื่อบังคับให้ผลิตภัณฑ์คาร์ทีเซียนของทุกแถวและเรายังดึงคอลัมน์ทั้งหมด:
select s.*, t.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
order by OrderField;
+
| GroupId | MaxRank | GroupId | OrderField | foo | Rank |
+
| 10 | 3 | 10 | 10 | NULL | 7 |
| 20 | 6 | 10 | 10 | NULL | 7 |
| 10 | 3 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 30 | foo | 9 |
| 10 | 3 | 10 | 30 | foo | 9 |
| 10 | 3 | 20 | 40 | NULL | 10 |
| 20 | 6 | 20 | 40 | NULL | 10 |
| 10 | 3 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 60 | foo | 12 |
| 10 | 3 | 20 | 60 | foo | 12 |
+
เราสามารถเห็นได้จากด้านบนว่าอันดับสูงสุดต่อกลุ่มนั้นถูกต้อง แต่ @Rank ยังคงเพิ่มขึ้นเมื่อประมวลผลตารางที่ได้รับที่สองเป็น 7 และสูงกว่า ดังนั้นอันดับจากตารางที่ได้รับที่สองจะไม่ทับซ้อนกับอันดับจากตารางแรกที่ได้มาเลย
คุณต้องเพิ่มตารางที่ได้รับมาอีกเพื่อบังคับให้ @Rank รีเซ็ตเป็นศูนย์ระหว่างการประมวลผลตารางทั้งสอง (และหวังว่าเครื่องมือเพิ่มประสิทธิภาพจะไม่เปลี่ยนลำดับในการประเมินตารางมิฉะนั้นจะใช้ STRAIGHT_JOIN เพื่อป้องกันสิ่งนั้น):
select s.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (select @Rank := 0) r
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField;
+
| GroupId | OrderField | foo | Rank |
+
| 10 | 30 | foo | 3 |
| 20 | 60 | foo | 6 |
+
แต่การเพิ่มประสิทธิภาพของแบบสอบถามนี้แย่มาก ไม่สามารถใช้ดัชนีใด ๆ สร้างตารางชั่วคราวสองตารางเรียงลำดับอย่างยากลำบากและยังใช้บัฟเฟอร์การรวมเนื่องจากไม่สามารถใช้ดัชนีเมื่อเข้าร่วมตารางชั่วคราวได้เช่นกัน นี่คือตัวอย่างผลลัพธ์จากEXPLAIN
:
+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+
| 1 | PRIMARY | <derived4> | system | NULL | NULL | NULL | NULL | 1 | Using temporary; Using filesort |
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | |
| 1 | PRIMARY | <derived5> | ALL | NULL | NULL | NULL | NULL | 6 | Using where; Using join buffer |
| 5 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 4 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
| 2 | DERIVED | <derived3> | ALL | NULL | NULL | NULL | NULL | 6 | Using temporary; Using filesort |
| 3 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
+
ในขณะที่วิธีการแก้ปัญหาของฉันโดยใช้การรวมภายนอกด้านซ้ายจะปรับให้เหมาะสมดีกว่า ไม่ใช้ตารางอุณหภูมิและแม้แต่รายงาน"Using index"
ซึ่งหมายความว่าสามารถแก้ไขการรวมโดยใช้เพียงดัชนีโดยไม่ต้องสัมผัสข้อมูล
+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+
| 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 1 | SIMPLE | t2 | ref | GroupId | GroupId | 5 | test.t1.GroupId | 1 | Using where; Using index |
+
คุณอาจจะอ่านคนที่อ้างสิทธิ์ในบล็อกของพวกเขาว่า "join make SQL slow" แต่นั่นเป็นเรื่องไร้สาระ การเพิ่มประสิทธิภาพที่ไม่ดีทำให้ SQL ช้า