distinct作用于多列时是对所有指定列组成的整行(元组)去重,仅当各列值完全相同时才视为重复;null被视为相等,大小写、空格敏感,不可部分去重。

DISTINCT作用于多列时到底去重什么
DISTINCT 不是对某列单独去重,而是对整行(即所有指定列组成的元组)去重。比如 SELECT DISTINCT a, b FROM t,会把 (1, 'x') 和 (1, 'y') 当作两条不同记录保留,因为组合值不同。
- 去重依据是所有列的值完全一致:只有当
a和b同时相等,才视为重复 - NULL 参与比较时按 SQL 标准处理:两个 NULL 被认为相等(在大多数数据库如 PostgreSQL、SQL Server、MySQL 8.0+ 的默认模式下)
- 如果只写
SELECT DISTINCT a,那b列根本不会参与判断,也无法出现在结果中(语法报错)
想保留某条件下的“最新一条”,不能只靠DISTINCT
DISTINCT 没有选择逻辑——它不关心哪条该留、哪条该删,只机械地筛掉完全重复的行。如果你的需求是“每个用户取最新订单”或“每类商品取价格最高的那条”,DISTINCT 无能为力。
- 这类需求本质是分组后取聚合结果,得用
GROUP BY+ 窗口函数或子查询 - 错误做法:
SELECT DISTINCT user_id, MAX(order_time) FROM orders—— 缺少GROUP BY user_id会报错(除非 MySQL 5.7 以下且sql_mode放宽) - 正确思路之一:用
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time DESC)标记序号,外层筛选rn = 1
多个字段去重但想顺便拿其他列数据,怎么办
SELECT DISTINCT a, b 只能返回这两列。如果还想带出 c 或 id,直接加进去会导致去重粒度变细(比如原本 (a,b) 重复的行,加上 c 后可能全都不重复了)。
- 常见错误:加
id后发现“没去重”——因为每行id都不同,DISTINCT自然不起作用 - 解决方法取决于业务意图:
- 要任意一条对应记录:用
GROUP BY a, b配合MIN(id)或MAX(id) - 要确定某条(如最新):先用窗口函数排序,再
GROUP BY a, b取MAX(time)对应的整行(需关联或用FIRST_VALUE()) - 在 PostgreSQL 中可考虑
DISTINCT ON (a, b) ORDER BY a, b, time DESC,这是特有语法,非标准 SQL
- 要任意一条对应记录:用
性能和索引影响容易被忽略
多列 DISTINCT 在大数据量下可能很慢,尤其当涉及文本、JSON 或未索引字段时。
- 数据库通常需要排序或哈希整个结果集才能判重,内存/磁盘开销大
- 如果已有复合索引覆盖
(a, b),且查询只查这两列,部分引擎(如 PostgreSQL)可能走索引扫描避免排序 - 避免在
DISTINCT中使用函数或表达式(如DISTINCT UPPER(name)),这会让索引失效,且无法利用前缀索引优化
实际执行前,用 EXPLAIN 看是否出现 Sort 或 HashAggregate,再决定是否要加索引或改写逻辑。










