表 posts(id, tid, subject, message, dateline),需查每個主題(tid)的最新回覆(dateline 最大)。
直覺寫法:
SELECT * FROM posts GROUP BY tid LIMIT 10;
錯誤結果:返回每組**第一行**(最早回覆),非最新。
MySQL GROUP BY 不保證組內行順序,預設取組內第一行(通常是最早插入)。ORDER BY 在 GROUP BY 之後執行,無法影響組內選取。
SELECT * FROM (
SELECT * FROM posts ORDER BY dateline DESC
) AS p
GROUP BY tid
ORDER BY dateline DESC
LIMIT 10;
原理:內層按時間倒序,外層 GROUP BY 取每組第一行即最新。
SELECT p.* FROM posts p
JOIN (
SELECT tid, MAX(dateline) AS max_dt FROM posts GROUP BY tid
) latest ON p.tid = latest.tid AND p.dateline = latest.max_dt
ORDER BY p.dateline DESC
LIMIT 10;
WITH ranked AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY tid ORDER BY dateline DESC) AS rn
FROM posts
)
SELECT * FROM ranked WHERE rn = 1 ORDER BY dateline DESC LIMIT 10;
| 方法 | 適用版本 | 效能 | 備註 |
|---|---|---|---|
| 子查詢法 | 所有版本 | 中 | 大表需覆蓋索引 (tid, dateline) |
| JOIN 聚合 | 所有版本 | 較好 | 利用索引掃描,推薦 5.7 |
| 窗口函數 | 8.0+ | 最佳 | 語法清晰,優化器支援好 |
CREATE INDEX idx_tid_dateline ON posts(tid, dateline DESC);
-- 錯誤:order by 作用於分組後結果,不影響組內選取
SELECT * FROM posts GROUP BY tid ORDER BY dateline DESC;
-- 正確:用聚合函數
SELECT * FROM posts GROUP BY tid ORDER BY MAX(dateline) DESC;