表 loginlog 記錄用戶操作日誌,id 自增主鍵,username 重復。需求:一條 SQL 獲取每個用戶最新(id 最大)的一條記錄,包含 username、operate,結果不重復用戶。
SELECT a.*
FROM (
SELECT * FROM loginlog ORDER BY id DESC
) a
GROUP BY a.username;
ORDER BY id DESC 將全表按時間倒序(最新在前)。GROUP BY username 分組時,MySQL(開啟 ONLY_FULL_GROUP_BY 前)會**取每組第一行**(即最新記錄)的所有字段值。ONLY_FULL_GROUP_BY) 報錯,改用聚合函數或窗口函數(見下)。SELECT id, username, operate
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY username ORDER BY id DESC) AS rn
FROM loginlog
) t
WHERE rn = 1;
優點:標準、可移植、可擴展取「前 N 條」。MySQL 8.0+、PostgreSQL、SQL Server、Oracle 均支持。
SELECT l1.*
FROM loginlog l1
JOIN (
SELECT username, MAX(id) AS max_id
FROM loginlog
GROUP BY username
) l2 ON l1.username = l2.username AND l1.id = l2.max_id;
優點:利用索引 (username, id) 極快,無臨時表/文件排序。
SELECT l1.*
FROM loginlog l1
LEFT JOIN loginlog l2
ON l1.username = l2.username AND l1.id < l2.id
WHERE l2.id IS NULL;
缺點:大表性能差(笛卡爾積),不推薦生產環境。
CREATE INDEX idx_username_id ON loginlog(username, id DESC);
覆蓋 GROUP BY username、MAX(id)、ORDER BY id DESC 所有場景。
| MySQL 版本 | 推薦方案 | 理由 |
|---|---|---|
| 8.0+ | ROW_NUMBER() | 標準、可讀、擴展性強 |
| 5.7 / 5.6 | 派生表+GROUP BY (非嚴格模式) | 簡潔、利用倒序特性 |
| 5.7 / 5.6 (嚴格模式) | 關聯子查詢 MAX(id) | 標準、高性能、穩定 |
掌握「分組取最新」三大範式,應對 99% 同類需求。