select distinct对整行去重,非单列;仅当所有选定列值完全相同时才视为重复。多列去重时name重复但age不同则保留全行,单按name去重要只选该列;配合order by时排序字段须在select列表中;聚合中distinct只能用于函数内如count(distinct user_id)。

SELECT DISTINCT 会去重整行,不是单个字段
很多人以为 DISTINCT 是对某一个列去重,其实它作用于整个 SELECT 列表。只要两行在所有选中的列上完全一致,才会被当作重复行删掉。
常见错误现象:写成 SELECT DISTINCT name, age FROM users,结果发现 name 还是有重复——因为不同人的 name 相同但 age 不同,整行不重复,所以都保留了。
- 想只按
name去重?得明确只选name:SELECT DISTINCT name FROM users - 如果还要附带其他字段(比如取每个
name对应的最新一条记录),DISTINCT就不够用了,得换GROUP BY或窗口函数 - MySQL 8.0+ 支持
DISTINCT配合ORDER BY,但排序字段必须出现在SELECT列表中,否则报错:SELECT DISTINCT city FROM shops ORDER BY created_at会失败,因为created_at没被选中
DISTINCT 在聚合查询里不能和普通字段混用
一旦用了 COUNT、SUM 这类聚合函数,就不能再混着写非聚合字段,除非用 GROUP BY。这时候 DISTINCT 只能放在聚合函数内部,比如 COUNT(DISTINCT user_id)。
错误写法:SELECT DISTINCT user_id, COUNT(*) FROM orders —— 直接报错,语法不允许。
- 统计不同用户数:用
SELECT COUNT(DISTINCT user_id) FROM orders - 想同时看每个用户的订单数?必须
GROUP BY user_id,而不是靠DISTINCT -
DISTINCT在聚合函数里不改变分组逻辑,只是去重输入值;COUNT(DISTINCT x)和COUNT(x)结果通常不同,尤其当x有重复或NULL
DISTINCT 性能开销比想象中大
DISTINCT 实际上隐式触发排序或哈希去重,数据量大时可能显著拖慢查询。特别是多字段组合去重,内存占用和 CPU 消耗会上升。
- 避免在没索引的字段上用
DISTINCT,尤其是TEXT或长字符串列 - 如果只是校验“是否存在重复”,用
EXISTS (SELECT 1 FROM t1 a JOIN t1 b ON a.x = b.x AND a.id 往往比 <code>SELECT DISTINCT x FROM t1更快 - PostgreSQL 中可加
UNIQUE约束或物化视图预计算去重结果;MySQL 则建议在常用去重字段上建联合索引,加速DISTINCT执行
替代方案:DISTINCT 不是万能去重工具
真正需要“取每组第一条”“按条件保留某条”时,DISTINCT 完全无能为力。它只做集合去重,不提供选择逻辑。
比如“每个部门工资最高的人”,SELECT DISTINCT dept, MAX(salary) FROM emp GROUP BY dept 只能得到部门和最高薪,但不知道是谁——因为 MAX() 是聚合,原始行信息丢了。
- 这类需求该用窗口函数:
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) - 旧版 MySQL(5.7 及之前)不支持窗口函数,就得用自连接或子查询模拟,复杂度明显上升
- 别为了强行用
DISTINCT而拼接字段(如CONCAT(dept, '|', name)),可读性差、索引失效、还容易因分隔符冲突出错
真正难的从来不是语法怎么写,而是想清楚:你到底要的是“集合唯一性”,还是“按业务规则择优保留”。前者 DISTINCT 能扛,后者得换思路。










