distinct是select的修饰符而非函数,必须紧随select后且不加括号;它作用于整行去重,要求所有select字段值完全匹配才视为重复,null被视为相等,order by字段须出现在select列表中。

DISTINCT 不是函数,不能加括号
DISTINCT 是 SELECT 的修饰符,不是函数,写成 SELECT DISTINCT(name) 或 SELECT DISTINCT (id, name) 会直接报错(PostgreSQL 报 syntax error at or near "(",MySQL 报 ERROR 1064)。它必须紧贴 SELECT 后面,后面跟字段列表,例如:SELECT DISTINCT country, city FROM users。
常见错误现象:语法解析失败、IDE 红线标错、执行时报“unexpected token”。
-
DISTINCT作用于整行结果,不是某一个字段 - 字段间用逗号分隔,不能用括号包裹
- 某些旧版 MySQL 对
SELECT和DISTINCT之间的换行或空格敏感,建议写成SELECT DISTINCT a, b而非换行缩进
去重逻辑:只认整行完全一致
DISTINCT 判断重复的唯一标准是:所有 SELECT 出来的列值逐位相等。哪怕只差一个空格、1 毫秒时间戳、大小写差异(在区分大小写的数据库如 PostgreSQL 中),都算不同行。
典型误用:SELECT DISTINCT name FROM users 返回多个同名用户——这不是 bug,是因为这些行在其他未选字段(比如 created_at、email)上实际不同;数据库内部仍生成了多行,只是投影后你“看不见”差异。
- NULL 值在所有主流数据库中均被视为相等,
(a=1, b=NULL)和(a=1, b=NULL)会被合并为一行 - 但
NULL和空字符串''永远不相等,混用会导致意外保留重复 - 若业务要求区分 NULL(如“未知”和“未填写”),需提前转换:
COALESCE(status, CONCAT('UNKNOWN_', id)) - 字符串隐式差异(尾部空格、不可见字符)可用
TRIM()+LOWER()预处理
ORDER BY 字段必须出现在 SELECT 列表中
用了 DISTINCT 后,ORDER BY 只能引用 SELECT 中明确写出的字段或别名。否则多数数据库(尤其是启用了 ONLY_FULL_GROUP_BY 的 MySQL 8.0+)会报错 ERROR 1055。
错误示例:SELECT DISTINCT name FROM users ORDER BY created_at —— created_at 没出现在 SELECT 中,无法排序。
- 解决方法一:把排序字段加进 SELECT,但注意这会改变去重粒度(变成按
(name, created_at)去重) - 解决方法二:用子查询封装去重结果,再对外层排序:
SELECT name FROM (SELECT DISTINCT name FROM users) t ORDER BY name - PostgreSQL 支持非标准的
DISTINCT ON,可写SELECT DISTINCT ON (name) name, created_at FROM users ORDER BY name, created_at DESC,但不可跨数据库移植
DISTINCT vs GROUP BY:语义不同,不能随便替换
SELECT DISTINCT a, b FROM t 和 SELECT a, b FROM t GROUP BY a, b 结果可能一样,但底层逻辑和约束完全不同。
DISTINCT 是纯结果集去重操作,不支持聚合;GROUP BY 是分组机制,天然允许 COUNT(*)、MAX(created_at) 等聚合表达式。
- 想统计每个
name出现次数?必须用GROUP BY:SELECT name, COUNT(*) FROM users GROUP BY name -
SELECT DISTINCT name, COUNT(*) FROM users语法非法,直接报错 - 大数据量时,
GROUP BY在有合适索引的情况下可能比DISTINCT更快(如 MySQL 松散索引扫描);但无索引时,DISTINCT的哈希去重可能更轻量 - 若只需去重列表,不用后续聚合,优先用
DISTINCT—— 语义清晰,意图明确
DISTINCT 总是在最后一步才动手,中间膨胀的数据早就在内存里跑了一圈。











