數據去重是查詢中高頻需求。MySQL提供DISTINCT和GROUP BY兩種機制,二者在功能、性能、結果集形態上存在本質差異。本文深度對比並給出選型建議。
SELECT DISTINCT column_name FROM table_name;
SELECT DISTINCT column1, column2 FROM table_name; -- 多列組合去重
SELECT列表中所有列的組合去重表user數據:
uid | username
1 | 小李
2 | 小張
3 | 小李
4 | 小王
5 | 小李
6 | 小張
SELECT DISTINCT username FROM user;
-- 結果:
username
小李
小張
小王
局限:無法同時返回uid等其他列。若強行SELECT DISTINCT username, uid,則按組合去重,username去重失效。
SELECT column_list FROM table_name GROUP BY column1, column2, ...;
標準SQL要求:SELECT列表僅能包含聚合函數或GROUP BY中的列。
MySQL擴展(默認ONLY_FULL_GROUP_BY關閉時):
SELECT列表包含未分組、未聚合的列ORDER BY)GROUP BY中的列SELECT * FROM user GROUP BY username;
-- 結果(取每組第一行):
uid | username
1 | 小李
2 | 小張
4 | 小王
完美解決「去重並保留完整行」需求!
SELECT * FROM user GROUP BY username, uid;
-- 等同於無分組,因為uid唯一,返回所有行
| 維度 | DISTINCT | GROUP BY |
|---|---|---|
| 返回列 | 僅去重列 | 可返回任意列(MySQL擴展) |
| 去重粒度 | SELECT列表整體組合 | GROUP BY指定列組合 |
| 結果行數 | 去重後唯一組合數 | 分組數 |
| 可配合聚合 | 不可直接聚合 | 原生支持COUNT/SUM/AVG等 |
| 排序控制 | 需外層ORDER BY | 可內部ORDER BY控制取哪行 |
| 標準兼容性 | 標準SQL | MySQL擴展(開啟ONLY_FULL_GROUP_BY受限) |
| 性能特點 | 內部常轉換為GROUP BY執行 | 可利用索引分組,支持聚合下推 |
利用MySQL擴展,在分組內排序後取第一行:
-- 取每個用戶最新的一條訂單
SELECT * FROM (
SELECT * FROM orders ORDER BY user_id, created_at DESC
) t GROUP BY user_id;
-- 或 MySQL 8.0+ 窗口函數(標準、確定)
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) rn
FROM orders
) t WHERE rn = 1;
GROUP BY列建立索引,避免臨時表+文件排序sql_mode),強制編寫標準兼容SQL,避免非確定性結果需要去重?
├─ 僅需去重列本身,無聚合需求 → DISTINCT (簡潔、標準)
└─ 需保留其他列 / 需聚合統計 / 需控制取哪行 → GROUP BY
├─ MySQL 8.0+ → 窗口函數 ROW_NUMBER() (最標準、確定)
├─ 老版本 + 非嚴格模式 → GROUP BY + 子查詢ORDER BY (常用技巧)
└─ 嚴格模式(ONLY_FULL_GROUP_BY) → 聚合函數(ANY_VALUE/MAX) 或 子查詢JOIN
DISTINCT是輕量級去重工具,適用於簡單場景GROUP BY是強大的分組聚合引擎,MySQL擴展使其成為「保留完整行去重」的首選