SQL的DISTINCT按整行去重,非单列;NULL被视为相同值;ORDER BY字段须出现在SELECT中;无法指定保留哪行,替代方案为GROUP BY或窗口函数。
很多人误以为 DISTINCT 是对某个字段单独去重,其实它作用于整个 SELECT 结果行。只要两行在所有被选中的列上完全一致,才会被合并为一行。
比如 SELECT DISTINCT name, age FROM users,不会只看 name 去重,而是看 (name, age) 这个组合是否重复。哪怕 name 相同但 age 不同,也会保留两条记录。
name 取唯一值,应写成 SELECT DISTINCT name FROM users
age(比如最新/最小),不能靠 DISTINCT 实现,得用 GROUP BY 或窗口函数DISTINCT 会在执行末期触发排序或哈希去重,可能影响性能,尤其在大数据集 + 多列场景下SQL 标准规定:所有 NULL 在去重时被认为是相等的。也就是说,多行中若某列全为 NULL,它们会被当作重复行合并。
例如:SELECT DISTINCT status FROM orders,若表中有 5 行 status 是 NULL,结果里只会出现一个 NULL。
WHERE status = NULL 不同(后者永远不成立),但 DISTINCT 对 NULL 的处理是确定且一致的DISTINCT ON (col) 语法,可控制保留哪一行,但标准 SQL 不支持'UNKNOWN')代替 NULL
当使用 ORDER BY 时,排序字段必须出现在 SELECT 列表中——前提是用了 DISTINCT。否则多数数据库(如 PostgreSQL、SQL Server)会报错;MySQL 8.0+ 也默认启用该检查。
错误示例:SELECT DISTINCT name FROM users ORDER BY created_at → 报错,因为 created_at 没出现在 SELECT 中。
created_at 加进 SELECT(但会改变去重粒度),要么改用 GROUP BY name ORDER BY MAX(created_at)
ORDER BY 无法安全地基于未选中的列排序sql_mode 设置),但行为不可靠,不建议依赖真正想“取每组第一条”时,DISTINCT 往往不是正确工具。它不保证返回哪一条,也不支持指定优先级。
比如“每个部门取薪资最高的人”,写成 SELECT DISTINCT dept, MAX(salary) FROM emp GROUP BY dept 是对的;但

SELECT DISTINCT dept, name, salary FROM emp ORDER BY salary DESC,结果既不确定,也无法保证 name 和 MAX(salary) 匹配。
GROUP BY + 聚合函数,或 ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)
DISTINCT 无法跳过某些列参与去重(比如忽略时间戳只按业务主键去重),此时必须用子查询或 CTE 预处理DISTINCT 容易掩盖笛卡尔积问题——先检查是否有多对多关联导致行数异常膨胀