mysql 5.7+ 中 json_object 与 json_arrayagg 可嵌套使用,需内层用 group by + json_arrayagg 聚合多行数据为数组,再传入外层 json_object;若子查询无结果,须用 coalesce(..., json_array()) 补空数组。

MySQL 5.7+ 的 JSON_OBJECT 和 JSON_ARRAYAGG 怎么配合嵌套查询用?
直接上结论:能,但必须严格控制子查询返回单行或聚合结果,否则 JSON_OBJECT 会报错 Subquery returns more than 1 row。核心思路是把多行数据先用 JSON_ARRAYAGG 聚合成一个 JSON 数组,再塞进外层 JSON_OBJECT。
常见错误是写成这样:
SELECT JSON_OBJECT('id', id, 'tags', (SELECT tag FROM tags WHERE post_id = posts.id)) FROM posts;
子查询没聚合,MySQL 拒绝执行。正确做法是:
- 内层用
GROUP BY posts.id+JSON_ARRAYAGG(JSON_OBJECT('name', tag)) - 外层再套一层
SELECT JSON_OBJECT('post_id', id, 'tags', tag_list) - 注意:
JSON_ARRAYAGG本身会忽略 NULL,但若子查询无匹配结果,它返回NULL,不是空数组 —— 需要COALESCE(..., JSON_ARRAY())补位
PostgreSQL 中 json_build_object 和 json_agg 的嵌套边界在哪?
PostgreSQL 更灵活,但陷阱藏在 JOIN 方式里。用 LEFT JOIN LATERAL 是安全的,直接 JOIN 多对多会导致重复展开,json_agg 会包进重复项。
比如查文章及其分类、标签:
- 错误写法:
SELECT json_build_object('title', t.title, 'categories', json_agg(c.name), 'tags', json_agg(k.name)) FROM topics t JOIN categories c ON ... JOIN tags k ON ... GROUP BY t.id—— 笛卡尔积导致数组膨胀 - 正确写法:先用
LATERAL分别聚合,或拆成三个独立子查询用COALESCE((SELECT ...), '[]'::json) -
json_build_object不自动转义字符串,若字段含双引号或反斜杠,得提前用replace()或确认应用层已处理
SQL Server 的 FOR JSON 能否支持多层嵌套对象?
可以,但只支持两级:主表用 FOR JSON PATH,子表必须用 SELECT ... FOR JSON PATH 作为标量子查询,且需加 AS [key] 别名才能被外层识别为字段值。
典型结构:
SELECT id, title,
(SELECT name FROM categories c WHERE c.post_id = p.id FOR JSON PATH, WITHOUT_ARRAY_WRAPPER) AS category,
(SELECT tag FROM tags t WHERE t.post_id = p.id FOR JSON PATH) AS tags
FROM posts p
FOR JSON PATH, ROOT('posts')
-
WITHOUT_ARRAY_WRAPPER用于单对象场景,避免套一层[] - 子查询返回空时,整个字段为
NULL,不是空字符串 —— 若前端要求非空,得用ISNULL(..., 'null')或拼接默认 JSON 字符串 -
FOR JSON AUTO无法控制嵌套结构,强制按 JOIN 顺序生成,不推荐用于复杂 JSON
嵌套 JSON 生成后,为什么前端解析失败或字段丢失?
大概率是 SQL 层没处理 NULL 和类型隐式转换。数据库字段为 NULL 时,JSON_OBJECT(MySQL)或 json_build_object(PG)会跳过该键,而 FOR JSON(SQL Server)会保留键但值为 null —— 行为不一致。
- MySQL:用
IFNULL(field, '')或COALESCE(field, '')统一补空字符串,避免键消失 - PostgreSQL:
json_build_object接收 NULL 值,但会输出"key": null,前端需兼容 - 所有方言:数值型字段如
price DECIMAL直接进 JSON 可能变成字符串(取决于驱动),显式转CAST(price AS FLOAT)更稳妥 - 最易忽略的是字符集:如果数据库用
utf8mb4但连接未设charset=utf8mb4,emoji 或生僻字会变???










