DISTINCT未去重是因为它对整行字段组合全匹配去重,若SELECT中含id等唯一字段则必然不重复;NULL被视为相同值可合并;需确保业务定义的“重复”(如大小写、空格)已预处理。

为什么 DISTINCT 有时没去重?检查字段是否真“重复”
DISTINCT 去重是按**整行值完全一致**判断的,不是按某个字段。如果查询中包含 id、created_at 等唯一或高区分度字段,那结果几乎不可能重复——哪怕业务上你只关心 name 和 email。
常见错误现象:SELECT DISTINCT name, email, id FROM users 返回大量“重复姓名”,因为每条记录的 id 都不同。
实操建议:
- 只 SELECT 真正需要去重的字段,比如
SELECT DISTINCT name, email FROM users - 若需保留某条具体记录(如最新一条),
DISTINCT无能为力,得换GROUP BY+ 聚合函数或窗口函数 - 注意 NULL 值:SQL 标准中,
NULL = NULL为 UNKNOWN,但DISTINCT把所有 NULL 视为相同值,会合并
DISTINCT 和 GROUP BY 能互换吗?看场景选
语法上,SELECT DISTINCT a, b FROM t 和 SELECT a, b FROM t GROUP BY a, b 结果通常一样,但语义和限制完全不同。
使用场景差异:
- 纯去重且不计算:优先用
DISTINCT,语义清晰,多数引擎优化更好 - 要去重同时统计数量、取最大时间等:必须用
GROUP BY,例如SELECT city, COUNT(*) FROM users GROUP BY city - MySQL 5.7+ 严格模式下,
GROUP BY要求 SELECT 列要么在 GROUP BY 中,要么被聚合函数包裹;DISTINCT无此限制
性能问题:DISTINCT 在大数据量下为什么会变慢?
DISTINCT 本质是隐式排序或哈希去重,需要把中间结果全加载进内存或临时磁盘表。数据量越大,I/O 和 CPU 开销越明显。
可优化点:
- 确保去重字段上有索引,尤其是组合索引顺序匹配
SELECT DISTINCT col1, col2的顺序 - 避免在
DISTINCT前用函数或表达式,例如SELECT DISTINCT UPPER(name)无法走name索引 - 如果只是校验“是否存在重复”,用
SELECT 1 FROM t GROUP BY a, b HAVING COUNT(*) > 1 LIMIT 1比SELECT DISTINCT更快
ORDER BY 和 DISTINCT 一起用的坑
DISTINCT 发生在 ORDER BY 之前。这意味着:你不能在 ORDER BY 中引用未出现在 SELECT DISTINCT 列表里的字段,否则报错(如 PostgreSQL、SQL Server)或行为不确定(如旧版 MySQL)。
错误示例:SELECT DISTINCT name FROM users ORDER BY created_at DESC —— created_at 没出现在 SELECT 中,多数数据库直接拒绝。
正确做法:
- 把排序依据也放进
DISTINCT字段列表(如果业务允许):SELECT DISTINCT name, created_at FROM users ORDER BY created_at DESC - 或改用子查询/CTE 先去重,再排序:
SELECT name FROM (SELECT DISTINCT name FROM users) t ORDER BY name - 注意:加了
ORDER BY后,DISTINCT的执行计划可能从哈希变为排序,影响性能
真正难的不是写 DISTINCT,而是想清楚“重复”的定义是否和业务一致——比如邮箱大小写是否算不同、空格要不要 trim、NULL 和空字符串是否等价。这些都得在去重前处理干净,而不是指望 DISTINCT 自动识别。










