-- 編寫順序
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...
-- 真實執行順序
FROM ... → WHERE ... → GROUP BY ... → HAVING ... → SELECT ... → ORDER BY ...
致命後果:GROUP BY 在 SELECT 前執行,分組內**只取第一行** (非確定性,通常是物理存儲順序第一行),隨後 ORDER BY 僅對這些「分組後的第一行」排序,**而非分組內最新記錄**!
-- 需求:每個分類 (category_id) 取最新文章 (date 最大)
SELECT * FROM test GROUP BY category_id ORDER BY `date` DESC;
-- 結果:每組取物理第一行,再按 date 排序 → **非最新記錄**
SELECT * FROM (
SELECT * FROM `test` ORDER BY `date` DESC
) AS temp
GROUP BY category_id
ORDER BY `date` DESC;
原理:內層先按 date DESC 排序,使每組物理第一行 = 最新記錄;外層 GROUP BY 取各組第一行 → 即最新記錄。
-- 步驟 1:每組取最大 date (或 id)
SELECT category_id, MAX(`date`) AS max_date FROM `test` GROUP BY category_id;
-- 步驟 2:關聯回原表取完整行
SELECT t.*
FROM `test` t
JOIN (
SELECT category_id, MAX(`date`) AS max_date FROM `test` GROUP BY category_id
) tm ON t.category_id = tm.category_id AND t.`date` = tm.max_date;
-- 注意:若 date 有重複值,會返回多行。可改用 MAX(id) 假設 id 遞增
SELECT * FROM `test`
WHERE id IN (
SELECT SUBSTRING_INDEX(
GROUP_CONCAT(id ORDER BY `date` DESC), -- 組內按 date 倒序拼接 id
',', 1 -- 取第一個 (最新)
)
FROM `test`
GROUP BY category_id
)
ORDER BY `date` DESC;
注意:GROUP_CONCAT 默認最大長度 1024 字節 (group_concat_max_len),大組數據會截斷導致錯誤。需調大:SET SESSION group_concat_max_len = 1000000;
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY `date` DESC) AS rn
FROM `test`
) t
WHERE rn = 1;
優勢**:標準 SQL、單掃描、無臨時表、支持並行查詢、語義清晰。**8.0+ 生產環境首選方案**。
| 方案 | MySQL 版本 | 性能 | 適用場景 |
|---|---|---|---|
| 子查詢先排序 | 所有 | 中 (需臨時表/文件排序) | 小中表、臨時查詢 |
| 聚合函數 JOIN | 所有 | 好 (利用索引) | 有索引 (category_id, date)、大表 |
| GROUP_CONCAT 技巧 | 5.0+ | 中 (受 group_concat_max_len 限制) | 單表、組數較小、無合適索引 |
| 窗口函數 ROW_NUMBER | 8.0+ | **最佳** | **生產環境首選**、大表、複雜分組邏輯 |
SELECT * FROM (
SELECT * FROM `ecm_copy_goods_change` ORDER BY `cid` DESC
) `ecm_copy_goods_change`
GROUP BY goods_id
ORDER BY `cid` DESC;