การเลือก Top-N พร้อมจัดเรียงรองด้วย Subquery และ ORDER BY
เมื่อต้องการเลือกข้อมูล Top-N ตามคอลัมน์หนึ่ง แล้วจัดเรียงผลลัพธ์ตามอีกคอลัมน์หนึ่ง ให้ใช้ Subquery (Derived Table) เพื่อกรอง Top-N ก่อน แล้วค่อยจัดเรียงอีกครั้ง
รูปแบบคำสั่ง
SELECT * FROM (
SELECT columns FROM table_name
ORDER BY primary_sort_column DESC, secondary_sort_column ASC
LIMIT N
) AS subquery_alias
ORDER BY secondary_sort_column;
ตัวอย่าง: เลือก 3 คนคะแนนสูงสุด แล้วจัดเรียงตามชื่อ A-Z
1. สร้างตารางและเพิ่มข้อมูล
CREATE TABLE DemoTable (
Name VARCHAR(40),
Score INT
);
INSERT INTO DemoTable VALUES ('Chris', 45);
INSERT INTO DemoTable VALUES ('Bob', 98);
INSERT INTO DemoTable VALUES ('David', 78);
INSERT INTO DemoTable VALUES ('Mike', 96);
INSERT INTO DemoTable VALUES ('Carol', 43);
2. ดูข้อมูลทั้งหมด
SELECT * FROM DemoTable;
ผลลัพธ์:
+-------+-------+
| Name | Score |
+-------+-------+
| Chris | 45 |
| Bob | 98 |
| David | 78 |
| Mike | 96 |
| Carol | 43 |
+-------+-------+
3. Query หลัก: Top 3 Score แล้วเรียงชื่อ A-Z
SELECT * FROM (
SELECT Name, Score
FROM DemoTable
ORDER BY Score DESC, Name ASC
LIMIT 3
) AS tbl
ORDER BY Name;
ผลลัพธ์:
+-------+-------+
| Name | Score |
+-------+-------+
| Bob | 98 |
| David | 78 |
| Mike | 96 |
+-------+-------+
การทำงานทีละขั้นตอน
- Subquery (ภายใน): เรียงตาม Score DESC (มากไปน้อย), Name ASC (อักษร) → ได้ Bob(98), Mike(96), David(78) แล้ว LIMIT 3
- Outer Query (ภายนอก): นำ 3 แถวที่ได้มา เรียงใหม่ตาม Name ASC → Bob, David, Mike
เหตุผลที่ต้องใช้ Subquery
หากเขียน ORDER BY Score DESC, Name ASC LIMIT 3 แล้วตามด้วย ORDER BY Name ในระดับเดียว MySQL จะใช้ ORDER BY ตัวสุดท้าย ทำให้ Top 3 ไม่ถูกต้อง
MySQL 8.0+ : ใช้ CTE (Common Table Expression) อ่านง่ายกว่า
WITH TopScores AS (
SELECT Name, Score
FROM DemoTable
ORDER BY Score DESC, Name ASC
LIMIT 3
)
SELECT * FROM TopScores ORDER BY Name;
กรณีคะแนนเท่ากัน (Tie-breaking)
ใน Subquery ใช้ ORDER BY Score DESC, Name ASC เพื่อให้คะแนนเท่ากัน เรียงตามชื่ออักษร ก่อน LIMIT
การใช้งานกับ Window Functions (MySQL 8.0+)
SELECT Name, Score FROM (
SELECT Name, Score,
ROW_NUMBER() OVER (ORDER BY Score DESC, Name ASC) AS rn
FROM DemoTable
) t WHERE rn <= 3
ORDER BY Name;