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 之间不相等的一般规则并不矛盾,统计时要留意这一行为是否符合业务定义。