SQL Server 无原生JSON_ARRAYAGG函数,须用子查询+FOR JSON实现数组聚合,如SELECT dept_name, (SELECT name,role FROM emp WHERE dept_id=d.id FOR JSON AUTO) AS staff_json FROM dept d GROUP BY d.id,dept_name。

SQL Server 不存在 JSON_ARRAYAGG 函数,直接写 JSON_ARRAYAGG 会报错“对象名 'JSON_ARRAYAGG' 无效”。你必须用 FOR JSON 配合子查询 + GROUP BY 实现等效效果,或改用 STRING_AGG 拼接后手动包 JSON 结构(不推荐)。
SQL Server 2016+ 正确做法:用 FOR JSON 在子查询中展开再聚合
SQL Server 不支持原生数组聚合函数,FOR JSON 是唯一可靠路径。它本质是把每组结果转成 JSON 数组字符串,不是生成真正的 JSON 类型值(注意类型是 NVARCHAR(MAX),不是 JSON)。
常见错误是试图在主查询里直接 SELECT ... FOR JSON 而没嵌套子查询,导致语法报错或结果错乱。
- 必须用子查询包裹分组逻辑,外层再加
FOR JSON;例如统计每个部门的员工列表: SELECT d.dept_name, (SELECT e.name, e.role FROM employees e WHERE e.dept_id = d.id FOR JSON AUTO) AS staff_json FROM departments d GROUP BY d.id, d.dept_name;-
FOR JSON AUTO自动推导结构,但字段名不能含点号(如e.name可,e.contact.email不行);需显式别名:e.contact_email AS [contact.email] - 空组返回
NULL,不是空数组[];若需强制返回空数组,得用ISNULL(..., '[]')包裹子查询 - 子查询里不能用
ORDER BY(除非加TOP 100 PERCENT),否则FOR JSON报错
为什么不用 OPENJSON 做聚合?
OPENJSON 是解析函数,不是聚合函数。它把 JSON 字符串“展开”成行,适合反向操作(比如把 JSON 数组炸开后做 GROUP BY),但无法把多行聚合成一个 JSON 数组。
典型误用:SELECT JSON_VALUE(items, '$[0].id') FROM orders GROUP BY items —— 这只是取首元素,不是聚合。
- 想对 JSON 数组字段(如
orders.items)做统计,先用OPENJSON(items)展开,再GROUP BY原表主键,最后在外层用FOR JSON封装结果 - 漏掉
WITH子句时,OPENJSON返回key/value/type三列,无法直接聚合数值字段;必须写明路径:WITH (item_id INT '$.id', qty INT '$.qty') -
OPENJSON不自动验证 JSON 合法性;若字段含非法 JSON,查询直接报错,建议前置ISJSON(items) = 1过滤
SQL Server 2022+ 的新选项:JSON_OBJECTAGG 只适用于键值对,不适用于数组
JSON_OBJECTAGG 是 SQL Server 2022 引入的,但它只接受两个参数(key 和 value),输出是 JSON 对象 {"k1":"v1","k2":"v2"},**不能生成数组 [...]**。
如果你的数据天然是键值结构(如配置项 {"timeout":30,"retries":3}),可以用它;但面对订单明细这种数组结构,它完全不适用。
- 错误尝试:
SELECT JSON_OBJECTAGG('id', item_id) FROM OPENJSON(...) WITH (item_id INT '$.id')→ 返回对象,不是数组 - 强行模拟数组:用
STRING_AGG拼'[' + STRING_AGG(...) + ']',但需手动转义引号、处理NULL、兼容中文——极易出错,生产环境不建议 - 真正需要数组语义时,坚持用子查询 +
FOR JSON AUTO或FOR JSON PATH,后者控制力更强(可自定义根节点、包装字段)
最易被忽略的一点:SQL Server 的 JSON 功能全部依赖数据库兼容级别 ≥ 130,且 FOR JSON 在子查询中不能引用外部作用域的聚合别名(如 SELECT ..., (SELECT ... GROUP BY outer.id) FOR JSON 中的 outer.id 必须显式传入子查询 WHERE 条件)。跨层级引用失效是调试时最常见的卡点。











