DISTINCT 对 SELECT 后所有字段组合去重,区分空格、NULL、大小写;多列时需全部值相同才去重;不可部分去重,替代方案为 GROUP BY 或窗口函数。

MySQL DISTINCT 作用范围到底按什么算
DISTINCT 不是简单地“删掉重复行”,而是对 SELECT 后面列出的所有字段组合做唯一性判断。哪怕只多一个空格、一个 NULL、大小写不同,都算不同值。
- 单列去重:
SELECT DISTINCT name FROM users—— 只看name字段值是否重复 - 多列去重:
SELECT DISTINCT city, district FROM addresses—— 只有city和district同时完全相等,才算重复 - NULL 被视为相同值:两行都是
NULL在district字段上,会被去重合并成一行 - 不能只对部分字段去重再选其他字段 —— 下面这句语法错误:
SELECT DISTINCT name, email FROM users想只按name去重但保留所有email?不行,必须明确你要的是哪组组合的唯一性
用 GROUP BY 替代 DISTINCT 的真实场景
当你要“按某列去重,但顺带取该组里最新/最早/任意一条记录的其他字段”时,DISTINCT 无能为力,必须换 GROUP BY + 聚合函数或子查询。
- 常见错误写法:
SELECT DISTINCT user_id, last_login_time FROM logs—— 这只是取出所有唯一的(user_id, last_login_time)对,不是每个用户只留一条 - 正确做法(取每个用户的最新登录时间):
SELECT user_id, MAX(last_login_time) FROM logs GROUP BY user_id - 如果还想取那条完整记录(比如 IP、设备),就得用窗口函数或关联子查询,
DISTINCT完全不适用 -
GROUP BY在 MySQL 5.7+ 默认开启ONLY_FULL_GROUP_BY,所以不能SELECT *加GROUP BY a,这点比DISTINCT严格得多
DISTINCT 性能开销在哪,什么时候会拖慢查询
DISTINCT 本质是隐式排序 + 去重,MySQL 通常会建临时表或用临时内存结构来排重,字段越多、数据量越大,越容易触发磁盘临时表,性能断崖下跌。
- 索引能加速:如果
DISTINCT字段上有联合索引,且覆盖了全部 SELECT 列(即“覆盖索引”),可避免回表和临时表 - 别在大文本字段上用:
DISTINCT long_text_column会让 MySQL 无法使用索引,还可能因字段太长强制用磁盘临时表 - 替代思路:如果只是校验去重后数量,用
COUNT(DISTINCT col)比SELECT DISTINCT col再数行数快得多 - 注意
ORDER BY:加了ORDER BY之后,MySQL 往往要先排重再排序,双重开销;如非必要,别和DISTINCT一起用
遇到 “Duplicate entry” 错误,DISTINCT 解不了
DISTINCT 是查询时去重,不影响数据写入。如果你插入时遇到 ERROR 1062 (23000): Duplicate entry 'xxx' for key 'PRIMARY',说明主键或唯一索引冲突,这是 DML 层问题,不是 SELECT 层问题。
- INSERT 场景想避免重复:用
INSERT IGNORE、ON DUPLICATE KEY UPDATE或先SELECT再判断,而不是指望DISTINCT - UPDATE 多行时也一样:
UPDATE ... SET x=1 WHERE id IN (SELECT DISTINCT id FROM tmp)—— 这里的DISTINCT只影响子查询结果,不解决更新本身是否违反唯一约束 - 真正要查“哪些值会冲突”,得用
GROUP BY + HAVING COUNT(*) > 1,而不是靠DISTINCT看表面有没有重复
很多人卡在“以为 DISTINCT 能控制数据逻辑”,其实它只是结果集投影层的一个开关。真要处理重复数据的业务含义——比如保留最新、合并字段、跳过冲突——得靠 GROUP BY、窗口函数、应用层判断,或者干脆改表结构加唯一约束。










