在未使用聚合函數(SUM、MIN、MAX 等)的情況下,GROUP BY 的排序行為在 MySQL 與 SQLite 中存在關鍵差異。這直接影響「取得每群組最新一筆資料」這類常見查詢的寫法。本文深入剖析兩者差異並提供正確解法。
假設表 tb_push_data 儲存推播訊息,需依 groupid 分組取得最新時間 (created_date) 的一筆資料。
SELECT * FROM (
SELECT t.id, t.data_id, t.send_username, t.recv_username, t.message,
IFNULL(NULLIF(t.groupid, ''), t.recv_username) AS groupid,
t.created_date
FROM tb_push_data t
WHERE STATUS = 0
ORDER BY t.created_date DESC, t.id DESC
) t1
GROUP BY t1.groupid
ORDER BY t1.created_date DESC, t1.id DESC;
SELECT * FROM (
SELECT t.id, t.data_id, t.login_username, t.send_username, t.recv_username, t.message,
IFNULL(NULLIF(t.groupid, ''), t.send_username) AS groupid,
t.time
FROM tb_recved_data t
WHERE status = 0
ORDER BY t.time ASC, t.id ASC
) t1
GROUP BY t1.groupid
ORDER BY t1.time DESC, t1.id DESC;
| 資料庫 | 子查詢排序 | 保留策略 | 結果 |
|---|---|---|---|
| MySQL | DESC(最新在前) | 忽略重複,保留第一筆 | 得到最新資料 ✓ |
| SQLite | ASC(最舊在前) | 覆蓋重複,保留最後一筆 | 得到最新資料 ✓ |
關鍵洞察:MySQL 保留「第一次遇到」的列,所以子查詢必須讓最新資料排在最前;SQLite 保留「最後一次遇到」的列,所以子查詢必須讓最新資料排在最後。
MAX(created_date) 搭配 JOIN 或窗口函數(MySQL 8.0+),避免依賴非標準行為-- 使用 ROW_NUMBER() 窗口函數 (MySQL 8.0+, SQLite 3.25+)
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY groupid ORDER BY created_date DESC) AS rn
FROM tb_push_data
WHERE status = 0
) t WHERE rn = 1;
理解 MySQL「保留首筆」與 SQLite「保留尾筆」的本質差異,是撰寫正確 GROUP BY 查詢的關鍵。現代應用建議優先使用窗口函數,獲得標準、可移植且語意清晰的解決方案。