開發常遇到需求:依特定欄位組合去重,並保留符合條件的單筆記錄(如 ID 最大或最小)。DISTINCT 僅能處理「所有欄位完全相同」的情況,無法針對部分欄位去重。本文示範如何結合聚合函數與 GROUP BY 達標。
假設資料表 mytable:
| ID | username | password | TRDESC |
|---|---|---|---|
| 1 | A | abcdef | QR |
| 2 | A | abcdef | W34 |
| 3 | A | bbbbbb | AD |
| 4 | B | aaaaaa | asdf |
需求:username + password 組合去重,保留 ID 最大(或最小)的那一筆。
SELECT *
FROM mytable
WHERE ID IN (
SELECT MAX(ID)
FROM mytable
GROUP BY username, password
);
SELECT *
FROM mytable
WHERE ID IN (
SELECT MIN(ID)
FROM mytable
GROUP BY username, password
);
GROUP BY username, password 將相同組合分成一組。MAX(ID) / MIN(ID) 在每組內取出極值 ID。WHERE ID IN (...) 篩選出完整欄位的該筆記錄。關鍵限制:GROUP BY後的SELECT清單只能包含分組欄位或聚合函數(MAX、MIN、COUNT、SUM 等),否則 MySQL(ONLY_FULL_GROUP_BY 模式)會報錯。
使用 ROW_NUMBER() 更直觀、效能更佳:
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY username, password ORDER BY ID DESC) AS rn
FROM mytable
)
SELECT * FROM ranked WHERE rn = 1;
PARTITION BY username, password 依組合分組。ORDER BY ID DESC 組內按 ID 遞減排序,第一筆即為最大 ID。WHERE rn = 1 保留每組第一筆。(username, password) 建立複合索引,加速分組與排序。ORDER BY 改為對應時間欄位。掌握「部分欄位去重 + 保留極值記錄」模式,可解決 90% 以上去重業務需求。MySQL 8.0 視窗函數寫法更優雅、可維護性更高,建議優先採用。