CREATE TABLE aa (
id INT PRIMARY KEY,
SName VARCHAR(20),
ClsNo INT, -- 班級
Score INT -- 分數
);
-- 每班 4 人,共 3 班
INSERT INTO aa VALUES
(1,'張三',1,90),(2,'李四',1,85),(3,'王五',1,80),(4,'趙六',1,75),
(5,'錢七',2,95),(6,'孫八',2,90),(7,'周九',2,85),(8,'吳十',2,80),
(9,'鄭一',3,100),(10,'王二',3,95),(11,'李三',3,90),(12,'張四',3,85);
SELECT a.id, a.SName, a.ClsNo, a.Score FROM aa a LEFT JOIN aa b ON a.ClsNo = b.ClsNo AND a.Score < b.Score GROUP BY a.id, a.SName, a.ClsNo, a.Score HAVING COUNT(b.id) < 2 ORDER BY a.ClsNo, a.Score DESC;
COUNT >= 2 被 HAVING 過濾SELECT * FROM aa a
WHERE 2 > (
SELECT COUNT(*) FROM aa
WHERE ClsNo = a.ClsNo AND Score > a.Score
)
ORDER BY a.ClsNo, a.Score DESC;
SELECT * FROM aa
WHERE id IN (
SELECT id FROM aa WHERE ClsNo = a.ClsNo ORDER BY Score DESC LIMIT 2
) ORDER BY ClsNo, Score DESC;
-- ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
WITH ranked AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY ClsNo ORDER BY Score DESC) AS rn
FROM aa
)
SELECT id, SName, ClsNo, Score
FROM ranked
WHERE rn <= 2
ORDER BY ClsNo, Score DESC;
| 方案 | 執行時間 | 掃描行數 | 適用版本 | 推薦指數 |
|---|---|---|---|---|
| 自連接 + HAVING | ~120ms | 全表掃描 + 臨時表 | 所有版本 | ⭐⭐⭐ |
| 相關子查詢 | ~200ms | 逐行子查詢 | 所有版本 | ⭐⭐ |
| ROW_NUMBER() | ~15ms | 索引掃描 + 窗口 | 8.0+ | ⭐⭐⭐⭐⭐ |
-- 覆蓋索引:分組列 + 排序列 + 選擇列 CREATE INDEX idx_cls_score ON aa (ClsNo, Score DESC, id, SName);
ROW_NUMBER() 會任意打斷並列,RANK() 保留並列名次 (可能超過 N 條),DENSE_RANK() 連續名次。按業務選擇。分組 Top-N 是 SQL 經典難題。**MySQL 8.0 ROW_NUMBER() 徹底解決**。5.7 環境下,**自連接 + HAVING** 是最穩健的通用方案;**相關子查詢** 適合小數據集。建立覆蓋索引 (分組列, 排序列 DESC, 選擇列) 是所有方案的性能基石。