必须用 distinct on 而非 group by 时,是需每组取排序后首行完整记录(如最新订单),因其无需聚合函数、避免 group by 非分组字段报错,且配合匹配索引性能更优;group by 无法直接保证返回哪条原始行。

什么时候必须用 DISTINCT ON 而不是 GROUP BY
当你需要“每组取一行完整记录”,且该行由明确排序决定(比如最新、最高、最早)时,DISTINCT ON 是最直接、最安全的选择。它不依赖聚合函数,也不要求你为非分组字段指定 MAX() 或 ANY_VALUE(),避免了语义模糊和报错风险。
-
DISTINCT ON (user_id)+ORDER BY user_id, created_at DESC→ 每个用户只返回最新一条订单的全部字段 - 等价写法若用
GROUP BY,需配合子查询或窗口函数,代码更长、可读性差,且容易漏掉ORDER BY导致结果随机 - MySQL 或 SQL Server 不支持
DISTINCT ON,但 PostgreSQL 中它是原生语法,执行计划通常生成Unique节点,比GroupAggregate开销更低
GROUP BY 无法替代 DISTINCT ON 的典型错误场景
常见误操作是试图用 GROUP BY user_id 直接选 id, created_at, amount 等非聚合字段——PostgreSQL 会直接报错:column "xxx" must appear in the GROUP BY clause or be used in an aggregate function。这不是配置问题,而是语义强制校验。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
- 想保留“每组第一条”的原始行?
GROUP BY本身做不到;它只保证分组存在,不控制哪条被选中 - 即使关掉
sql_mode或改用ANY_VALUE(),结果仍是不确定的,无法复现“按时间取最新”这类业务逻辑 -
DISTINCT ON的ORDER BY前缀必须严格匹配,否则报错;而GROUP BY对ORDER BY没有这种约束,但代价是失去结果确定性
性能差异关键在索引与执行节点
DISTINCT ON 的执行效率高度依赖联合索引是否覆盖 DISTINCT ON 列 + ORDER BY 前缀列。没有索引时,它会走全表扫描 + 排序 + 去重三步;有匹配索引时,可跳过排序,直接流式去重。
- 推荐索引:
CREATE INDEX idx_user_created ON orders (user_id, created_at DESC) - 对比
GROUP BY:即使有相同索引,优化器仍可能选择HashAggregate或Sort + GroupAggregate,内存占用更高,且无法跳过排序阶段 - 大数据量(如百万级)下,
DISTINCT ON在有索引时通常比等效GROUP BY + ROW_NUMBER()快 20%–40%,因为少一次窗口计算开销
DISTINCT ON 的硬性约束不能绕过
DISTINCT ON 不是语法糖,是 PostgreSQL 强制执行的语义规则。它要求 ORDER BY 必须以 DISTINCT ON 的字段开头,顺序、数量都要一致。漏掉、颠倒、多加字段都会导致报错或未定义行为。
- 错:
DISTINCT ON (a, b) ORDER BY a, c→ 缺少b,报错 - 错:
DISTINCT ON (a) ORDER BY b, a→ 前缀不匹配,报错 - 对:
DISTINCT ON (a, b) ORDER BY a, b, c DESC→ 允许追加,但前缀必须完全一致 - 即使数据当前“看起来有序”,没写
ORDER BY就用DISTINCT ON,结果就是不可靠的——下次执行可能换行
DISTINCT ON 的排序字段顺序必须和索引列顺序完全一致,否则优化器大概率弃用索引,直接退化成磁盘排序。










