
本文介绍在 PostgreSQL 中使用窗口函数 row_number() 配合整数除法实现“每组最多 N 个值”的分组聚合,替代单次 string_agg() 的超长拼接,确保每行输出严格控制在指定数量(如 4 个)以内。
本文介绍在 postgresql 中使用窗口函数 `row_number()` 配合整数除法实现“每组最多 n 个值”的分组聚合,替代单次 `string_agg()` 的超长拼接,确保每行输出严格控制在指定数量(如 4 个)以内。
在实际业务中,常需将同一逻辑分组下的多条记录(如样本编号 sample_no)聚合成逗号分隔的字符串。但若直接使用 string_agg(a2.sampl_no, ', ') 配合 GROUP BY,可能产生过长单行结果(例如包含 10+ 个编号),不便于展示、解析或下游系统处理。此时,目标应转为:将原始数据按顺序每 4 条划分为一个子组,再对每个子组独立聚合。
核心思路是引入动态分组标识(grp):利用 ROW_NUMBER() OVER (ORDER BY ...) 生成连续序号,再通过 (序号 - 1) / 4(整数除法)实现“每 4 行归为一组”。该表达式可确保序号 1–4 → 组 0,5–8 → 组 1,9–12 → 组 2,依此类推。
以下为完整可执行示例(基于表 rec_agg,字段为 sample_no):
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
WITH mk_grp AS (
SELECT
sample_no,
((ROW_NUMBER() OVER (ORDER BY sample_no)) - 1) / 4 AS grp
FROM rec_agg
)
SELECT STRING_AGG(sample_no, ' ,') AS sampl_nos
FROM mk_grp
GROUP BY grp
ORDER BY grp;
✅ 关键说明:
- ORDER BY sample_no 确保分组顺序可控(若需按插入顺序,请使用带时间戳或自增 ID 的排序依据);
- (row_number() - 1) / 4 是 PostgreSQL 整数除法,自动向下取整,比 CEIL(row_number() / 4.0) 更简洁高效;
- ORDER BY grp 在最终查询中显式添加,保证输出按分组序号升序排列(避免因哈希分组导致乱序);
- 若需括号包裹结果(如 (0001 ,0002 ,0003 ,0004)),可在 STRING_AGG 外层包装:
SELECT '(' || STRING_AGG(sample_no, ' ,') || ')' AS sampl_nos
⚠️ 注意事项:
- 此方案依赖有序性,务必在 ROW_NUMBER() 的 ORDER BY 子句中指定明确、稳定的排序列;
- 若原始数据量极大,建议在排序列上建立索引以加速窗口函数执行;
- 不适用于需要“跨分组保持语义关联”(如按业务类型再细分)的复杂场景——此时应先按主维度 GROUP BY,再对各组内结果应用本方法。
通过该技巧,你可灵活控制聚合粒度,使输出结构清晰、可读性强,同时完全在数据库层完成计算,减少应用层数据处理负担。










