STRING_AGG不指定排序即未定义行为,结果顺序完全不可控;PostgreSQL须写STRING_AGG(expr, sep ORDER BY col),SQL Server须用WITHIN GROUP(ORDER BY col),外层ORDER BY无效。

不指定排序就是未定义行为,结果顺序完全不可控——不是“偶尔乱”,而是“随时可能变”,上线后出问题只是时间问题。
STRING_AGG 不加 ORDER BY 就是未定义行为
PostgreSQL 和 SQL Server 都明确要求:STRING_AGG 本身不承诺任何拼接顺序。它不按插入顺序、不按主键顺序、不按索引物理位置拼接,只依赖当前执行计划中行的临时输出顺序。并行扫描、Vacuum 后页重组、统计信息更新、甚至客户端 fetch size 变化,都可能让同一语句两次执行返回不同字符串。
- 错误写法:
STRING_AGG(name, ', ')—— 没有ORDER BY,永远别信测试环境里“看起来有序” - PostgreSQL 正确写法:
STRING_AGG(name, ', ' ORDER BY created_at DESC)(ORDER BY必须紧贴分隔符后,且在函数括号内) - SQL Server 正确写法:
STRING_AGG(name, ', ') WITHIN GROUP (ORDER BY created_at DESC)(WITHIN GROUP是强制语法,不能省略)
外层 ORDER BY 对 STRING_AGG 完全无效
很多人把排序写在查询末尾,比如 SELECT STRING_AGG(name, ', ') FROM t GROUP BY dept ORDER BY dept,这只会让分组结果按 dept 排序,对每个分组内部的拼接顺序毫无影响。
- 现象:
dept = 'sales'这一组,第一次返回'Alice,Bob',第二次变成'Bob,Alice' - 原因:外层
ORDER BY不作用于聚合函数内部;只有函数内的排序子句才控制拼接逻辑 - 验证方式:用
EXPLAIN (ANALYZE)看执行计划,如果没看到Sort节点出现在聚合前,说明排序根本没生效
NULL 值和排序字段不匹配会放大乱序风险
即使写了 ORDER BY,如果排序字段含大量 NULL、或字段不在 GROUP BY 列表中,数据库可能退化为无序合并。
- PostgreSQL 中,
ORDER BY col要求col必须是GROUP BY中的列,或来自同级聚合表达式(如MAX(updated_at));否则报错 - SQL Server 中,
WITHIN GROUP (ORDER BY email)若email未出现在GROUP BY或聚合中,直接语法报错 - NULL 默认排最前(
NULLS FIRST),若业务希望空值靠后,必须显式写NULLS LAST,否则看似“稳定”的顺序其实隐含歧义
最容易被忽略的是视图或物化视图里的 STRING_AGG —— 一旦定义时漏掉排序子句,下游所有调用方都会继承这个不确定性,而你很难从调用侧发现根源在哪。

















