sql server生成嵌套json必须用for json path配合子查询并用json_query包裹,否则子查询结果会被转义为字符串;postgresql用json_agg+json_build_object组合;mysql需json_arrayagg+json_object拼接,三者空值处理和语法机制均不兼容。

SQL Server 中 FOR JSON PATH + 子查询是生成嵌套 JSON 的实际可行路径
SQL Server 原生不支持直接用 JSON_AGG(那是 PostgreSQL/Oracle 的函数),强行套用会报错 Invalid object name 'JSON_AGG'。必须用 FOR JSON PATH 配合相关子查询,再用 JSON_QUERY 包裹子结果,才能产出真正多层结构的 JSON。
常见错误现象:直接写 SELECT ..., (SELECT ... FOR JSON PATH) AS items FROM ... FOR JSON PATH,但子查询没加 JSON_QUERY(),结果里 items 字段变成带引号的字符串,而非内嵌对象数组。
- 子查询返回的是
NVARCHAR(MAX),不是 JSON 类型,必须显式包裹JSON_QUERY(...) - 外层
FOR JSON PATH不能自动识别子查询结果为 JSON,不包裹就转义成字符串 - 若子查询中字段别名含点号(如
detail.price),FOR JSON PATH会自动创建嵌套对象;但仅限一层,更深嵌套仍需子查询
PostgreSQL 里 JSON_AGG + 子查询组合才是标准做法
PostgreSQL 支持 JSON_AGG 和 JSON_BUILD_OBJECT 组合,能自然表达一对多嵌套。但要注意:子查询必须返回行集,不能直接 JSON_AGG 外层表字段——那只会聚合平铺数据。
使用场景:订单主表 + 明细表,要输出每个订单下多个 item 节点。
- 明细部分必须用独立子查询(或 LATERAL JOIN),再套
JSON_AGG(JSON_BUILD_OBJECT(...)) - 若在子查询中漏写
GROUP BY或关联条件,会导致笛卡尔积或重复聚合 -
JSON_AGG默认跳过 NULL,如有空明细,需配合CASE WHEN或左连接确保结构完整
示例片段(非完整语句):
SELECT o.id, o.order_date,<br> JSON_AGG(JSON_BUILD_OBJECT(<br> 'product_id', d.product_id,<br> 'qty', d.qty<br> )) AS items<br>FROM orders o<br>LATERAL (SELECT * FROM order_items d WHERE d.order_id = o.id) d<br>GROUP BY o.id, o.order_date;
MySQL 5.7+ 没有 JSON_AGG,得靠 JSON_OBJECT + JSON_ARRAYAGG 拼接
MySQL 不提供 JSON_AGG,但有 JSON_ARRAYAGG 和 JSON_OBJECT。问题在于:它不支持子查询直接返回 JSON 数组并嵌入外层对象——必须用派生表或 JOIN 拉平后再聚合成数组。
容易踩的坑:JSON_ARRAYAGG 对空集合返回 NULL,不是空数组 [];且无法在聚合前对子集做条件过滤(比如只取 status=1 的明细),必须提前 WHERE 或用 CASE 过滤字段值。
- 若明细为空,外层
JSON_OBJECT('items', JSON_ARRAYAGG(...))中items会是NULL,不是"items": [] - 想实现
"items": [{"id":1}, {"id":2}],必须先JOIN或LATERAL(8.0.14+)拉出明细行,再聚合 - 别名不能含点号,MySQL 的
FOR JSON不存在,所有 JSON 构造都靠函数拼,无自动嵌套推导
跨数据库移植时最易忽略的兼容性断点
同一个“订单含明细”需求,在 SQL Server、PostgreSQL、MySQL 上实现逻辑看似相似,但底层机制完全不同:SQL Server 依赖 FOR JSON 的语法驱动,PostgreSQL 依赖集合函数组合,MySQL 依赖字符串化构造。没有通用写法。
真正容易被忽略的点是 NULL 处理和空集合表现:
- SQL Server
FOR JSON默认忽略 NULL 字段,加INCLUDE_NULL_VALUES才显式输出"field": null - PostgreSQL
JSON_AGG对空子集返回NULL,需COALESCE(..., '[]'::json)补空数组 - MySQL
JSON_ARRAYAGG同样返回NULL,且无法用COALESCE直接转[],得用IF(COUNT(*)=0, '[]', JSON_ARRAYAGG(...))
别指望写一次 SQL 跑三端。嵌套 JSON 生成从来不是“写个查询就行”的事,而是每种方言都要单独验证结构、空值、编码和性能边界。











