首页/数据/数据库速查/DISTINCT 去重与去重优化
SQL 语法52 条

DISTINCT 去重与去重优化

对结果集整行去重;本质是排序或哈希去重,昂贵。想对单列去重通常可以用 GROUP BY 或 EXISTS 改写。

数据库速查数据更新于 2026-10-13

DISTINCT 是什么

DISTINCT 对结果集做整行去重,作用在 SELECT 列出的所有列的组合上,而不是只对第一列去重。实现上它通常需要排序或哈希去重,属于相对昂贵的操作。如果只是判断「是否存在」或「有哪些取值」,用 EXISTS、GROUP BY 或依赖索引往往代价更低。

基本信息

用途结果集去重
语法SELECT DISTINCT 列1[, 列2] …
作用范围所有被选列的组合,不是单列
替代方案GROUP BY、EXISTS、利用索引的松散扫描
成本需要临时表或排序,结果集大时代价明显

典型使用场景

  • 统计某段时间内的去重用户数(UV)
  • 导出不重复的维度取值清单供业务核对
  • 排查数据重复写入,确认唯一约束是否生效
  • 评估用 EXISTS 或 GROUP BY 改写能否提速
  • 对外报表里去掉重复明细行

用法与改写

用法与改写
SELECT DISTINCT user_id FROM orders;            -- 单列去重
SELECT DISTINCT city, status FROM orders;       -- 多列组合去重
SELECT COUNT(DISTINCT user_id) AS uv FROM orders
WHERE created_at >= '2026-10-01';               -- 去重计数

-- 只判断存在性时,EXISTS 通常更快
SELECT u.id FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);

-- 等价改写:能利用索引的松散扫描
SELECT user_id FROM orders GROUP BY user_id;

写法与代价

写法含义注意事项
DISTINCT col单列去重多列时是所有被选列的组合去重
COUNT(DISTINCT col)去重计数列上有索引时可能走松散扫描
GROUP BY col分组取值语义接近,还能配合 HAVING
EXISTS只判断存在避免全量去重的排序开销
DISTINCT 加 ORDER BY去重后再排序排序列不在选择列表时 8.0 报错
COUNT(DISTINCT col) 的资源消耗明显高于 COUNT:列上有索引时优化器可能利用索引避免建临时表,没有索引时就会建临时表做去重,大表上差别很大。估算成本时先看列上有没有索引,再决定要不要改写成 EXISTS 或 GROUP BY。
版本与 sql_mode 会影响 DISTINCT 与 ORDER BY 的组合:ORDER BY 的列若不在选择列表中,MySQL 8.0 会直接报错,迁移或升级前要专门验证这类语句,平时不容易触发,往往在真实业务 SQL 上才暴露。
DISTINCT 不会改变 NULL 的语义:多个 NULL 去重后只保留一个,这与 NULL 之间不相等的一般规则并不矛盾,统计时要留意这一行为是否符合业务定义。

易错点

  • 以为 DISTINCT 只作用于第一列:实际是所有被选列的组合去重,结果与预期不符
  • DISTINCT 与 ORDER BY 混用且排序列不在选择列表:8.0 直接报错
  • 用 DISTINCT 代替 EXISTS 做存在性判断:白白付出排序与临时表的代价
  • 对宽表多列做 DISTINCT:临时表体积超过内存限制后会落到磁盘,速度骤降
  • 把去重计数写成 COUNT(col):那只是统计非空行数,去重计数必须写 COUNT(DISTINCT col)

数据来源与参考

  • MySQL 8.0 Reference Manual: SELECT - DISTINCT Clause

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌