distinct on 与 group by 语义冲突,不可混用:前者按指定列分组后取排序首行完整记录,后者对每组进行聚合计算;postgresql 直接报错,必须根据需求二选一——要完整行用 distinct on,要聚合值用 group by。

DISTINCT ON 和 GROUP BY 不能混用在同一层查询中——它们语义冲突,PostgreSQL 会直接报错。 你不是“用 GROUP BY 与 DISTINCT ON 去重”,而是根据场景二选一:要聚合值就用 GROUP BY,要保留完整行就用 DISTINCT ON。
为什么不能同时写 GROUP BY 和 DISTINCT ON
PostgreSQL 不允许在同一个 SELECT 中同时出现 GROUP BY 和 DISTINCT ON。这不是限制,而是逻辑矛盾:
-
GROUP BY要求所有非分组字段必须用聚合函数包裹(如MAX()、STRING_AGG()),它输出的是汇总行 -
DISTINCT ON的目标是返回原始表中的某一行(不聚合),它依赖ORDER BY决定“哪一行” - 两者对“每组保留什么”的定义完全不同,引擎无法协调
DISTINCT ON 必须配 ORDER BY,且顺序严格对齐
这是最常踩的坑:漏写 ORDER BY,或字段顺序/数量不匹配,会导致报错或结果不可靠。
- 写
DISTINCT ON (user_id, status),就必须写ORDER BY user_id, status, created_at DESC—— 前两个字段顺序和数量必须完全一致 -
ORDER BY status, user_id会报错:ERROR: SELECT DISTINCT ON expressions must match initial ORDER BY expressions - 只写
ORDER BY user_id也会报错:缺少status字段 - 即使数据当前按
user_id自然有序,不显式声明ORDER BY,结果就是未定义的
GROUP BY 适合取聚合值,DISTINCT ON 适合取完整行
选哪个,取决于你要的结果形态:
- 要每个
product_id的最新价格和对应时间戳?用DISTINCT ON (product_id) ORDER BY product_id, updated_at DESC - 只要每个
product_id的最高价格?用GROUP BY product_id配MAX(price) - 要每个
product_id的最新价格 + 最低库存 + 平均销量?别硬套GROUP BY—— 这些字段不属于同一逻辑行,应拆成子查询或用DISTINCT ON加多字段排序保一致性 -
SELECT DISTINCT ON (a) a, b, MAX(c) FROM t GROUP BY a是非法语法,PostgreSQL 拒绝解析
索引不匹配会让 DISTINCT ON 变慢,甚至比 ROW_NUMBER() 还差
DISTINCT ON 的性能高度依赖索引,不是语法糖快就一定快。
- 如果经常执行
DISTINCT ON (region, category) ORDER BY region, category, score DESC,必须建联合索引:CREATE INDEX idx_region_cat_score ON items (region, category, score DESC) - 索引列顺序必须和
DISTINCT ON+ORDER BY前缀完全一致,否则优化器大概率不用 - 没索引时,100 万行以上数据的
DISTINCT ON查询可能触发大排序,磁盘临时文件暴涨,实际比等效的ROW_NUMBER()更慢 - 很多人忽略这点,只改语法不建索引,结果发现“升级写法后反而变慢了”
真正关键的不是语法选哪个,而是明确你要的是“一行原始记录”还是“一个聚合结果”。一旦混淆这个前提,后面所有优化都跑偏。










