distinct是select的修饰关键字,仅对查询结果整行去重,不能用于where或order by;它将select字段组合成元组判重,null被视为相同值;性能取决于索引与数据分布,执行计划需重点检查using temporary;它不修改原表,删重需用delete配合join或row_number()。

distinct 只能作用于 select 列表,不能用于 where 或 order by
很多人误以为 DISTINCT 是个独立操作符,可以像函数一样套在任意字段上,比如写成 WHERE DISTINCT user_id = 1——这会直接报错:ERROR 1064 (42000)。它本质是 SELECT 的修饰关键字,只对查询结果的整行去重生效。
实际去重逻辑是:MySQL 把 SELECT 后列出的所有字段拼成一个“元组”,再对这些元组做唯一性判别。所以 SELECT DISTINCT a, b 和 SELECT DISTINCT a 去重粒度完全不同。
-
SELECT DISTINCT id FROM users:只看id值是否重复 -
SELECT DISTINCT id, name FROM users:只有id和name都完全相同时才视为重复 - 加了
ORDER BY时,排序字段必须出现在SELECT列表中(除非启用了ONLY_FULL_GROUP_BY以外的 SQL 模式)
distinct 无法过滤 NULL,多个 NULL 被视为相同值
这是最容易被忽略的行为:MySQL 把所有 NULL 当作相等值处理。如果某列有 5 行是 NULL,SELECT DISTINCT col FROM t 最多只返回一个 NULL。
如果你需要保留全部非空值 + 显式标记“有 NULL 存在”,不能只靠 DISTINCT。常见应对方式:
- 用
COALESCE(col, 'N/A')替换NULL再去重(注意类型一致性) - 先查非空值:
SELECT DISTINCT col FROM t WHERE col IS NOT NULL,再单独查SELECT 1 FROM t WHERE col IS NULL LIMIT 1判断是否存在 - 若需统计去重后数量且包含 NULL 计数,改用
COUNT(DISTINCT col) + (CASE WHEN COUNT(*) > COUNT(col) THEN 1 ELSE 0 END)
distinct 性能差?别急着优化,先看执行计划
DISTINCT 在底层通常触发临时表(Using temporary)和文件排序(Using filesort),但是否真慢,得看数据分布和索引。
例如:SELECT DISTINCT status FROM orders,如果 status 是低基数字段(如只有 'pending'/'done'/'cancel'),即使千万级表,MySQL 也可能用松散索引扫描(Loose Index Scan)快速完成;但如果查的是 SELECT DISTINCT email FROM users,又没索引,那就必然走全表 + 临时表。
- 检查执行计划:
EXPLAIN SELECT DISTINCT ...,重点看Extra列是否含Using temporary - 给高频去重字段建单列索引,能显著减少临时表使用(尤其当该字段在
WHERE中也常用时) - 避免在
DISTINCT后混用函数或表达式,如SELECT DISTINCT UPPER(name)会强制计算+排序,无法利用name索引
真正要删重复数据?distinct 不是 delete 工具
DISTINCT 只影响查询结果,不会修改原表。想物理删除重复行,得用其他方法。
最稳妥的通用做法是借助主键或唯一标识:
- 假设表有自增主键
id,按业务字段(如email)去重并保留最小id:DELETE t1 FROM users t1 INNER JOIN users t2 WHERE t1.email = t2.email AND t1.id > t2.id;
- 无主键时,可用
ROW_NUMBER()(MySQL 8.0+):DELETE FROM users WHERE id IN ( SELECT id FROM ( SELECT id, ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) rn FROM users ) t WHERE rn > 1 ); - 切勿直接
CREATE TABLE AS SELECT DISTINCT ...后替换原表——会丢失索引、外键、权限、AUTO_INCREMENT 值等元信息
去重逻辑越复杂,越容易漏掉边界情况:比如时间精度不同导致看似相同的记录实际不重复,或者字符集/排序规则差异让两个字符串被判定为不同。动手前务必在测试库验证语义是否符合预期。










