distinct作用于多字段时,是对所有指定字段的组合值整体去重,仅当各字段值完全相同时才视为重复并保留一条。

DISTINCT作用于多字段时,到底去重哪部分?
DISTINCT不是对每个字段单独去重,而是对整个行组合去重。比如 SELECT DISTINCT a, b FROM t,会把 (1,'x') 和 (1,'y') 当作两条不同记录保留,但两个 (1,'x') 只留一个。
常见误解是以为它像 Excel 的“删除重复项”那样可选列,实际它始终基于 SELECT 后所有表达式构成的元组做唯一性判断。
- 如果想只按某字段去重,得用
GROUP BY或窗口函数配合ROW_NUMBER() -
DISTINCT ON (a)是 PostgreSQL 特有语法,但它要求必须配合ORDER BY a, ...,且只保留每组第一条——这不是标准 DISTINCT,别混用 - 字段顺序影响结果排序(如果没写 ORDER BY),但不影响去重逻辑
DISTINCT和ORDER BY一起用时,为什么总报错?
PostgreSQL 15 要求:如果用了 ORDER BY,排序字段必须出现在 SELECT 列表中,或能被 SELECT 中的表达式唯一确定。而 DISTINCT 会让这个限制更敏感。
例如 SELECT DISTINCT a FROM t ORDER BY b 会报错 ERROR: SELECT DISTINCT and ORDER BY cannot combine,因为 b 不在 SELECT 结果里,无法保证排序稳定。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
- 解决办法:把
ORDER BY字段加进 SELECT,再用COALESCE或别名隐藏(如SELECT DISTINCT a, b AS _order_hint FROM t ORDER BY b) - 或者改用子查询:
SELECT a FROM (SELECT DISTINCT a, b FROM t ORDER BY b) s - 注意:加了
DISTINCT后,ORDER BY的字段若含 NULL,可能影响去重后行序——NULL 被视为相等,但排序时默认排在最前/最后,取决于NULLS FIRST/LAST
性能差?可能是DISTINCT触发了HashAggregate而非IndexScan
PostgreSQL 15 对 DISTINCT 的执行计划选择很实在:没有合适索引时,直接走 HashAggregate,内存占用高、无法流式输出;有覆盖索引时,才可能用 Unique + Index Scan。
比如表 t(a int, b text, c timestamp),执行 SELECT DISTINCT a, b FROM t,即使 a 有索引,只要没建 (a,b) 复合索引,就大概率走 HashAggregate。
- 建索引优先考虑查询字段顺序:
CREATE INDEX idx_ab ON t(a, b) - 避免在 DISTINCT 字段里用函数或表达式,比如
SELECT DISTINCT lower(b) FROM t无法利用b的普通索引,得建函数索引:CREATE INDEX idx_lower_b ON t(lower(b)) - 数据量大时,
DISTINCT比GROUP BY略快(少解析分组逻辑),但差别微小,别为此强行改写
和NULL值打交道时,DISTINCT行为容易误判
PostgreSQL 认为所有 NULL 值相等,所以 (1, NULL) 和 (1, NULL) 会被去重;但 (1, NULL) 和 (1, 'x') 视为不同——这点和 SQL 标准一致,但和某些应用直觉冲突。
典型陷阱:用 SELECT DISTINCT status, category FROM orders 统计分类时,发现 NULL 类别被合并成一行,但你本意可能是把它当独立类别处理。
- 若需把 NULL 当普通值区分,无需额外操作——它本来就被当作相同值去重了
- 若想让 NULL 和非 NULL 分开统计(比如补上“未知”标签),用
CASE WHEN status IS NULL THEN 'unknown' ELSE status END替代原始字段 - 注意:
UNION也按同样规则处理 NULL,别指望用UNION替代DISTINCT来绕过这个问题
DISTINCT、DISTINCT ON 还是 GROUP BY;索引和 NULL 处理这两块,最容易在线上查着查着就慢下来或者结果不对。










