distinct 作用于整行而非单列,仅当所有字段值完全相同时才去重;null 被视为相同值;与聚合函数、group by 混用易出错;order by 只能引用 select 列表中的字段;性能开销大,需谨慎使用。

SELECT DISTINCT 只对整行去重,不是按某个字段
很多人以为 DISTINCT 是“去掉某列重复值”,实际它作用于整个 SELECT 行结果。只要任意一列不同,这行就被视为不重复。
比如 SELECT DISTINCT name, age FROM users,即使 name 相同但 age 不同,两条记录都会保留。
- 想只取唯一
name?得写成SELECT DISTINCT name FROM users,不能带其他可能变化的字段 - 如果必须同时返回
name和id,但只按name去重,DISTINCT就不够用,得换GROUP BY或窗口函数 - 数据库对
NULL的处理一致:多个NULL被视为相同值,会被合并为一行
DISTINCT 在聚合查询里会干扰 COUNT 和 GROUP BY
DISTINCT 和聚合函数混用时容易误判结果。最典型的是 COUNT(DISTINCT column) 和 COUNT(*) 完全不是一回事。
-
COUNT(*)统计所有行数;COUNT(column)忽略该列NULL后计数;COUNT(DISTINCT column)只统计该列非重复非空值个数 - 在
GROUP BY查询中加DISTINCT(如SELECT DISTINCT a, COUNT(b) FROM t GROUP BY a)语法通常报错——DISTINCT和GROUP BY逻辑冲突,数据库不允许这种组合 - MySQL 5.7+ 默认开启
ONLY_FULL_GROUP_BY,连“没出现在GROUP BY里的非聚合字段”都会报错,这时候硬加DISTINCT更容易触发错误
ORDER BY 必须用 SELECT 中出现的字段或表达式
用了 DISTINCT 之后,ORDER BY 子句能引用的字段受限——只能是 SELECT 列表里的字段、别名,或者基于它们的表达式。
- 错误示例:
SELECT DISTINCT name FROM users ORDER BY id——id没出现在SELECT里,多数数据库直接报错ERROR 1055(尤其 MySQL 8.0+) - 正确做法:要么把
id加进SELECT(但会破坏按name去重的本意),要么改用子查询或GROUP BY配合MIN(id)等聚合取值 - PostgreSQL 允许
ORDER BY引用未选字段,但行为不可移植,线上环境别依赖
DISTINCT 性能开销比想象中大,特别是大数据量 + 多字段
DISTINCT 底层一般靠排序或哈希去重,数据量一上来,内存和 CPU 消耗明显上升,还可能触发磁盘临时表。
- 字段越多、长度越长(比如含
TEXT或长字符串),排序/哈希成本越高;MySQL 对VARCHAR(255)字段默认只索引前 767 字节,DISTINCT却要完整比对 - 没有对应索引时,
DISTINCT无法利用索引加速,全表扫描不可避免 - 替代思路:如果只是前端展示需要去重,优先考虑应用层 dedupe;如果是统计口径要求,确认是否真要“物理去重”,还是用
GROUP BY+ 聚合更清晰可控
真正难的不是写 DISTINCT,而是搞清你到底想定义什么算“重复”——是业务意义上的唯一性,还是 SQL 层面的行级相等。后者往往掩盖了前者的问题。










