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






























