
本文介绍使用 ROW_NUMBER() 配合整数除法实现动态分组,再通过 STRING_AGG() 按每组最多 4 个样本号聚合,避免单行结果过长,适用于报表分页、标签打印等场景。
本文介绍使用 `row_number()` 配合整数除法实现动态分组,再通过 `string_agg()` 按每组最多 4 个样本号聚合,避免单行结果过长,适用于报表分页、标签打印等场景。
在实际业务中(如实验室样本管理、物流批次编号汇总),常需将大量 sample_no 按固定数量(如每组 ≤4 个)分段聚合为逗号分隔字符串。PostgreSQL 原生 STRING_AGG() 不支持“截断式分组”,但可通过窗口函数 + 数学分组巧妙实现。
核心思路是:
- 使用 ROW_NUMBER() OVER (ORDER BY ...) 为每条记录生成连续序号(注意:务必明确 ORDER BY,否则顺序不可控);
- 利用整数除法 ((row_number - 1) / N) 将序号映射到逻辑组号(N = 4),确保每 N 条记录落入同一组;
- 对生成的组号 grp 执行 GROUP BY,再调用 STRING_AGG() 完成分段聚合。
以下为完整可执行示例:
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
-- 示例数据准备(实际使用时替换为你的表和字段)
CREATE TABLE rec_agg (sample_no TEXT);
INSERT INTO rec_agg VALUES
('0001'), ('0002'), ('0003'), ('0004'),
('0005'), ('0006'), ('0007'), ('0008'),
('0009'), ('0010'), ('0011');
-- 按每组最多 4 个 sample_no 聚合
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; -- 推荐显式排序,保证输出顺序与分组逻辑一致
输出结果:
┌───────────────────────────┐ │ sampl_nos │ ├───────────────────────────┤ │ 0001, 0002, 0003, 0004 │ │ 0005, 0006, 0007, 0008 │ │ 0009, 0010, 0011 │ └───────────────────────────┘
✅ 关键注意事项:
- ORDER BY 在 ROW_NUMBER() 中不可省略——它决定了样本号的排列逻辑(如按录入时间用 created_at,按字典序用 sample_no);
- 整数除法 / 在 PostgreSQL 中对 INTEGER 类型自动截断,确保 (0-3)/4=0, (4-7)/4=1… 实现严格分块;
- 若需自定义分隔符(如空格+括号),可在 STRING_AGG() 中调整,例如:STRING_AGG('(' || sample_no || ')', ' ');
- 当原始数据存在重复或需关联其他表时,建议先 DISTINCT 或在 CTE 中完成 JOIN,再进行分组。
该方案完全在数据库层完成,无需应用层拆分,高效且易于集成到视图或报表 SQL 中。










