select distinct作用于select后所有字段组成的元组,即整行投影去重,而非单列或整表行;例如select distinct name, age对('alice',25)和('alice',30')视为不同记录。

DISTINCT 不是万能去重开关,它只对 SELECT 后面列出的字段组合生效,不是按“整行”或“某列”单独去重。
SELECT DISTINCT 作用范围到底是什么?
很多人以为 DISTINCT 是把结果里重复的某列值(比如 user_id)抹掉,其实它判断的是「整行投影」——也就是你写在 SELECT 后面的所有字段组成的元组是否完全相同。
- 如果写
SELECT DISTINCT name, age FROM users,那么('Alice', 25)和('Alice', 30)算两条不同记录,都会保留 - 如果只写
SELECT DISTINCT name FROM users,那所有同名但其他字段不同的记录,name只会出现一次 - 加了
ORDER BY时,排序字段必须出现在SELECT列表中(MySQL 8.0+ 和 PostgreSQL 要求严格;旧版 MySQL 允许绕过,但行为不可靠)
用 GROUP BY 替代 DISTINCT 的常见理由
当你需要去重的同时做聚合(比如统计每个部门人数),或者想控制“以哪几列为准去重”,GROUP BY 更明确、更可控。
-
SELECT DISTINCT dept FROM employees和SELECT dept FROM employees GROUP BY dept效果一样,但后者可自然延伸为SELECT dept, COUNT(*) FROM employees GROUP BY dept - 如果要去重并取每组最新一条记录(比如按
updated_at),DISTINCT完全无能为力,必须配合子查询或窗口函数 - 某些数据库(如 SQL Server)对
DISTINCT的执行计划优化不如GROUP BY成熟,大数据量时性能差异明显
DISTINCT 和 NULL 值的隐含规则
SQL 标准规定:所有 NULL 值在 DISTINCT 判断中视为相等。也就是说,只要两行对应字段都是 NULL,它们会被当成重复行合并。
- 例如
SELECT DISTINCT region FROM sales中,哪怕有 10 行region是NULL,结果里也只出现一个NULL - 如果你希望把
NULL当作普通值参与去重逻辑(比如区分“未填”和“明确为空”),得提前用COALESCE(region, 'MISSING')或CASE WHEN处理 - 注意:部分数据库(如 Oracle)对
NULL的比较行为略有差异,但DISTINCT下的合并行为基本一致
真正容易被忽略的是:DISTINCT 发生在 SELECT 投影之后、ORDER BY 之前。这意味着你不能靠它来“先排好序再取唯一值”,顺序不保证稳定,除非显式加上 ORDER BY —— 而 ORDER BY 字段又必须出现在 SELECT 列表里,这个约束常导致语句重写。











