把分组数据聚合成 JSON
接口经常需要一个对象带一个数组的结构,例如用户详情里带上他买过的商品名列表。这类结构在数据库侧就能拼好,省掉应用层再组装一次。MySQL 8 提供数组聚合与键值聚合两个函数;PostgreSQL 对应的是 json 与 jsonb 两套聚合函数。两边的差异集中在两点:元素顺序能不能在聚合里指定,以及空值是否进入结果数组。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| MySQL 函数 | 数组聚合函数与键值对象聚合函数各一个 |
| PostgreSQL 函数 | json 与 jsonb 各有一组,对象聚合另有专门函数 |
| 排序能力 | PostgreSQL 可在聚合内部直接指定元素顺序 |
| 结果类型 | PostgreSQL 的 jsonb 会做键标准化与去重,输出顺序不保留 |
典型使用场景
- 用户详情接口返回该用户购买过的商品名数组
- 订单列表页给每个订单带上明细条目,避免逐单再查一次
- 把多行标签聚合成一个数组字段,直接写入缓存或搜索结果
- 构建键值映射:按类型字段聚合出「类型到数量」的对象
- 报表导出时把多行明细压成一行 JSON,便于前端直接渲染
两库聚合示例
两库聚合示例
-- MySQL 8:把每个用户的商品名聚合成数组
SELECT user_id,
JSON_ARRAYAGG(product_name) AS products
FROM order_items
GROUP BY user_id;
-- MySQL 8:按名称聚合出键值对象(键重复时后面的覆盖前面的)
SELECT JSON_OBJECTAGG(product_name, qty) AS kv
FROM order_items WHERE user_id = 100;
-- PostgreSQL:聚合内部可以直接指定顺序
SELECT user_id,
json_agg(product_name ORDER BY product_name) AS products
FROM order_items
GROUP BY user_id;
-- PostgreSQL:键值对象聚合
SELECT jsonb_object_agg(product_name, qty) AS kv
FROM order_items WHERE user_id = 100;差异要点
| 对比项 | MySQL | PostgreSQL |
|---|---|---|
| 数组聚合 | 提供数组聚合函数 | json 与 jsonb 两套,jsonb 更适合索引与比较 |
| 对象聚合 | 提供键值聚合函数 | 提供键值聚合函数,空键会报错 |
| 元素顺序 | 聚合内不能指定,需先在子查询里排序 | 可在聚合内直接指定顺序 |
| 空值处理 | 两库行为不同,需用样例数据核对 | jsonb 输出会重排键,顺序不保留 |
| 结果可用性 | 结果是合法的 JSON 文本 | jsonb 可继续用运算符做筛选 |
排序这件事不要想当然:MySQL 的数组聚合不保证元素顺序,需要固定顺序时先在子查询里排好再聚合,并且用样例数据验证实际输出顺序;PostgreSQL 则可以直接把排序写进聚合表达式。
聚合结果可能很大。给接口一次性返回上万条明细会让 JSON 文本膨胀到几兆,网络传输与前端解析都吃不消,通常应限制条数或改为分页获取明细。