SELECT DISTINCT 去除的是 SELECT 后所有字段组合完全相同的重复行,包括 NULL 被视为相同、区分大小写和空格;单列值相同但其他列不同则不视为重复,无法实现“按某列去重并取任意/最新行”,此时需用 GROUP BY 或窗口函数。

SELECT DISTINCT 会去掉哪些重复行
DISTINCT 作用于 SELECT 后面的整个字段列表,不是单个字段。只要组合值完全相同,整行就被视为重复——哪怕只有一列不同,也会保留两行。
常见误解是以为 SELECT DISTINCT name 能“挑出唯一 name”,但实际若表里有 (name='Alice', age=25) 和 (name='Alice', age=30),这两行都会被保留,因为 (name, age) 组合不重复。
- 想按
name去重并取某条记录(比如最新一条),DISTINCT无能为力,得用GROUP BY+ 聚合函数或窗口函数 -
SELECT DISTINCT *效率低且易出错,尤其表字段多、含TEXT/JSON等不可比较类型时,部分数据库(如 PostgreSQL)直接报错cannot DISTINCT on type json - MySQL 允许
DISTINCT和ORDER BY混用,但ORDER BY字段必须出现在SELECT列表中;SQL Server 则更严格,要求ORDER BY必须是SELECT中的表达式或别名
DISTINCT 和 GROUP BY 去重效果一样吗
单纯去重时,SELECT DISTINCT a, b FROM t 和 SELECT a, b FROM t GROUP BY a, b 返回结果一致,但底层行为不同:前者是逻辑去重,后者是分组聚合(即使没写聚合函数,也隐式执行了分组)。
关键差异在语义和扩展性:
-
GROUP BY支持后续加COUNT(*)、MAX(created_at)等,DISTINCT不支持 - 某些场景下
GROUP BY可能触发索引优化(如GROUP BY a, b匹配联合索引),而DISTINCT更依赖排序或哈希去重,性能更不可控 - PostgreSQL 中
DISTINCT ON (a)是特例,它能按a去重并保留每组第一条(需配合ORDER BY),但这不属于标准 SQL,MySQL 和 SQL Server 都不支持
为什么加了 DISTINCT 还有重复数据
最常见原因是字段包含 NULL —— 大多数数据库中,NULL = NULL 为 UNKNOWN,因此 (1, NULL) 和 (1, NULL) 不被视为相同组合,DISTINCT 不会合并它们。
其他典型原因:
- 字符串末尾空格:SQL 标准规定
'abc '和'abc'在比较时视为相等(padding 规则),但某些数据库(如 MySQL 的utf8mb4_bin排序规则)区分空格,导致去重失效 - 大小写敏感:若列使用
_cs(case-sensitive)排序规则,'ABC'和'abc'被认为不同 - 浮点数精度:直接对
float或double列用DISTINCT,可能因计算误差导致本该相等的值显示为不同 - 隐式类型转换:比如
SELECT DISTINCT id FROM t WHERE id = '123',若id是整型,数据库可能先转成字符串再比对,影响去重逻辑
替代 DISTINCT 的轻量级去重方案
当明确知道只需保留某类“代表行”(如最新、最早、ID 最小),用 ROW_NUMBER() 窗口函数往往更可控,也避免 DISTINCT 的语义模糊问题。
例如:按 user_id 去重,取每人的最新订单
SELECT user_id, order_id, created_at
FROM (
SELECT user_id, order_id, created_at,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders
) t
WHERE rn = 1;
这个写法清晰表达了业务意图,且不受 NULL 或排序规则干扰;相比 DISTINCT,它还能顺便拿到原行的其他字段(如 amount),而 DISTINCT 若想带出 amount,就必须把它也放进去重字段列表,反而可能破坏去重要求。
真正难的从来不是“怎么去掉重复”,而是“重复的定义到底是什么”——DISTINCT 只认字面一致,业务里的“重复”往往需要上下文判断。










