首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

深度解析 MySQL GROUP BY 與 ORDER BY 執行順序陷阱:取分組最新記錄的正確姿勢

     編輯:MySQL 綜合教學

核心陷阱:SQL 編寫順序 ≠ 執行順序


-- 編寫順序
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 取各組第一行 → 即最新記錄。

正確方案二:聚合函數 + 子查詢 (標準 SQL、性能更穩)


-- 步驟 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 遞增

正確方案三:GROUP_CONCAT + SUBSTRING_INDEX 技巧 (MySQL 特有、單表掃描)


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;

方案四:窗口函數 (MySQL 8.0+ 首選,最優雅高效)


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_NUMBER8.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;
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved