MySQL 數據去重:DISTINCT 與 GROUP BY 的全面解析與實戰
引言
在MySQL數據庫日常操作中,數據去重是高頻需求。無論是統計唯一用戶數、篩選不重復的商品類別,還是清理冗余數據,都需要選擇合適的去重方式。當前主流的去重手段主要是DISTINCT關鍵字和GROUP BY子句。
對比維度 | DISTINCT | GROUP BY |
設計目標 | 單純篩選不重復記錄,無統計需求 | 按指定列分組,結合聚合函數做統計 |
作用范圍 | 作用于SELECT后的所有列(組合去重) | 作用于GROUP BY指定的列(分組依據) |
聚合函數支持 | 不支持(若用聚合函數,會對所有去重后記錄整體統計) | 必須支持(分組后需通過聚合函數處理每組數據) |
結果排序 | 不保證結果有序(需手動加ORDER BY) | MySQL 5.7 + 默認按分組列升序排序(可關閉) |
過濾時機 | 僅能通過WHERE過濾原始數據 | 可通過WHERE過濾原始數據,HAVING過濾分組后數據 |
使用場景 | 簡單去重(如唯一值查詢) | 分組統計(如按類別計數、求和) |
基礎概念
DISTINCT:專注去重
DISTINCT是MySQL中的一個關鍵字,核心作用是從查詢結果中篩選出所有不重復的記錄,它僅關注唯一性,不涉及任何分組邏輯。其語法結構非常簡潔,僅需在SELECT后添加,作用于所有查詢的列(注意:非單個列,而是列的組合)。
SELECT DISTINCT 列1, 列2, ...
FROM 表名
[WHERE 條件];GROUP BY:基于分組的聚合工具
GROUP BY是一個子句,核心作用是將表中的記錄按照指定列分組,同一組的記錄會被合并,后續可結合聚合函數(如COUNT()、SUM())進行統計計算。它的本質是分組 + 聚合,去重只是分組后的一個副作用(當分組列無重復時,每組僅一條記錄)。
SELECT 分組列, 聚合函數(列)
FROM 表名
[WHERE 條件]
GROUP BY 分組列
[HAVING 分組條件];偽代碼
場景 1:單純查詢唯一值(無統計)→ 優先用 DISTINCT
當需求僅為獲取某列或多列的不重復值,無任何統計計算時,DISTINCT是最優選擇 —— 語法更簡潔,語義更清晰,避免不必要的分組邏輯。
示例: 查詢user_order表中所有不重復的商品類別 + 用戶ID組合(即同一用戶購買的不同商品):
-- 正確:用DISTINCT直接去重組合列
SELECT DISTINCT user_id, product
FROM user_order;
-- 錯誤:用GROUP BY雖能實現,但語義冗余(無聚合需求)
SELECT user_id, product
FROM user_order
GROUP BY user_id, product;場景 2:分組統計(如計數、求和)→ 必須用 GROUP BY
當需求涉及按某列分組,統計每組數據時,DISTINCT完全無法替代GROUP BY,因為DISTINCT不支持分組后的聚合計算。
示例 1: 統計每個商品的銷售總量(假設表中新增quantity列記錄單訂單購買數量):
-- 正確:GROUP BY分組+SUM()聚合
SELECT product, SUM(quantity) AS total_sales
FROM user_order
GROUP BY product;
-- 錯誤:DISTINCT無法按商品分組統計,只能統計所有去重記錄的總量(無意義)
SELECT DISTINCT product, SUM(quantity) AS total_sales
FROM user_order;示例 2: 篩選訂單數大于2的用戶(需先分組統計,再過濾分組結果):
SELECT user_id, COUNT(order_id) AS order_count
FROM user_order
GROUP BY user_id
HAVING COUNT(order_id) > 2; -- HAVING過濾分組后結果,DISTINCT無此能力場景 3:多列去重 + 整體統計 → 兩者結合或選其一
當需求是先對多列去重,再對去重后的結果做整體統計時,可選擇DISTINCT直接作用于統計函數,或GROUP BY分組后再統計,需結合語義選擇。
示例: 查詢不重復的(用戶ID + 商品)組合數量(即統計用戶購買的不同商品總數):
-- 方案1:DISTINCT作用于COUNT(),簡潔直觀
SELECT COUNT(DISTINCT user_id, product) AS unique_user_product
FROM user_order;
-- 方案2:GROUP BY分組后再統計分組數,語義稍復雜
SELECT COUNT(*) AS unique_user_product
FROM (
SELECT user_id, product
FROM user_order
GROUP BY user_id, product
) AS temp;場景 4:大數據量去重 → 結合索引判斷性能
當處理百萬級以上數據時,DISTINCT和GROUP BY的性能差異主要取決于是否有合適的索引,而非語法本身。
- 若查詢列(
DISTINCT的列或GROUP BY的列)有聯合索引,兩者都會走索引掃描,性能接近; - 若無索引,兩者都需走全表掃描,但
GROUP BY可能因分組排序產生額外開銷(MySQL 5.7 +可通過SET sql_mode = ''關閉默認排序,優化性能)。
示例: 對user_order表的user_id列做去重查詢(數據量100萬條):
-- 1. 無索引時,兩者都走全表掃描,GROUP BY因默認排序更慢
SELECT DISTINCT user_id FROM user_order; -- 耗時約0.8s
SELECT user_id FROM user_order GROUP BY user_id; -- 耗時約1.2s(默認排序)
-- 2. 建立索引后(ALTER TABLE user_order ADD INDEX idx_user_id(user_id)),兩者性能接近
SELECT DISTINCT user_id FROM user_order; -- 耗時約0.1s(索引掃描)
SELECT user_id FROM user_order GROUP BY user_id; -- 耗時約0.12s(索引掃描)總結:一句話掌握選擇邏輯
通過前文分析,DISTINCT和GROUP BY的選擇可總結為一句話:單純去重找DISTINCT,分組統計用GROUP BY;大數據量看索引,冗余列查詢要避免。
- 當需求是獲取唯一值,無統計時,優先用
DISTINCT,語法簡潔、語義清晰; - 當需求是按列分組,做統計(計數、求和等)時,必須用
GROUP BY,DISTINCT無法替代; - 性能優化的核心是減少數據處理量:利用索引覆蓋掃描、提前過濾數據、避免無用列,而非糾結于語法本身。































