SQL Server 2022 中 JSON 函数与子查询协同需严格区分解析与构造场景:OPENJSON 必须搭配 CROSS/OUTER APPLY 使用;JSON_VALUE 不支持按行动态求值,须通过 APPLY 展开;FOR JSON PATH 嵌套需 JSON_QUERY 包裹子查询结果;JSON_OBJECT 参数须为标量;LEAST/GREATEST 须配合 TRY_CAST 防隐式转换错误;路径表达式大小写敏感且不支持变量拼接。

SQL Server 2022 中子查询和 JSON 函数能协同工作,但必须严格区分「JSON 解析」和「JSON 构造」两类场景;直接在 WHERE 或 SELECT 中嵌套 OPENJSON 而不配合 APPLY,90% 会报错或返回空。
OPENJSON 必须搭配 CROSS APPLY 才能解析字段中的 JSON 字符串
很多人试图写 WHERE JSON_VALUE(profile, '$.city') = '北京',但 profile 是表中一列时,这种写法只在 profile 为常量字符串时有效;一旦它来自行数据(如 SELECT ... FROM Employees e WHERE JSON_VALUE(e.Profile, '$.city') = '北京'),SQL Server 就会拒绝执行——因为 JSON_VALUE 不支持按行动态求值。真正可行的是:
- 用
CROSS APPLY OPENJSON(e.Profile, '$')把每行的 JSON 字符串展开成行集 - 必须加别名,例如
AS j,否则语法报错Incorrect syntax near '(' - 从
j中用JSON_VALUE(j.value, '$.city')或直接j.[value]提取字段(若已用 WITH 显式定义列) - 若原始 JSON 字段可能为 NULL,
CROSS APPLY会跳过整行;需保留原记录就改用OUTER APPLY,再补WHERE j.[value] IS NOT NULL
FOR JSON PATH + 子查询生成嵌套结构,JSON_QUERY 是关键包裹层
想把员工信息连同其技能数组一起转成 JSON 对象?不能只靠 FOR JSON PATH 外层语句。常见错误是直接写子查询并期望它自动变成内嵌数组,结果却是字符串转义:
- 子查询返回的是
NVARCHAR(MAX),不是 JSON 类型,必须显式用JSON_QUERY(子查询)告诉 SQL Server “这是合法 JSON,别转义” - 子查询里要用
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER避免多套一层[],否则外层再包 JSON_QUERY 会出错 - 若子查询可能为空(比如某员工没填 Skills),
JSON_QUERY(NULL)返回 NULL,外层 JSON 里对应字段就消失;要保持字段存在就得用ISNULL(JSON_QUERY(...), '[]') - 字段别名含点号(如
'skills.name')可触发自动嵌套,但仅限一层;更深结构仍需子查询嵌套
JSON_OBJECT 和 JSON_ARRAY 要慎用表达式上下文
JSON_OBJECT() 看似简单,但在子查询或聚合中容易踩类型陷阱:
- 参数必须是标量表达式,不能是子查询结果集;例如
'skills': (SELECT STRING_AGG(s.value, ',') FROM STRING_SPLIT(e.Skills, ','))是合法的,但'skills': (SELECT s.value FROM STRING_SPLIT(e.Skills, ','))会报错“子查询返回了多行” - NULL 值默认输出
"key": null;若想整个键消失,必须显式加ABSENT ON NULL,不能省略 - 中文字符无需额外处理,但字段值若含双引号或反斜杠,
JSON_OBJECT会自动转义,无需手动 REPLACE - 嵌套调用可行,如
JSON_OBJECT('meta': JSON_OBJECT('ts': GETDATE())),但层级过深会影响可读性和调试效率
LEAST / GREATEST 和 JSON 结合时注意隐式转换边界
SQL Server 2022 新增的 LEAST() 在 JSON 场景中常被误用于比较 JSON 提取值,但实际限制很硬:
-
LEAST(JSON_VALUE(e.Profile, '$.age'), 30)只有在$.age确实是数字字符串(如"28")时才安全;若它是"N/A"或缺失,JSON_VALUE返回 NULL,LEAST会忽略它,结果取决于其他参数 - 混合类型调用如
LEAST('10', 5)会把字符串隐式转为 int,但LEAST('abc', 5)直接报错,而 JSON_VALUE 返回的始终是字符串,所以务必先用TRY_CAST(... AS INT)做防护 -
LEAST不接受超过 254 个参数,也不支持MAX那样的列通配;想对 JSON 数组元素求最小值,得先用OPENJSON展开再聚合,不能一步到位
最易被忽略的一点:所有 JSON 函数的路径表达式(如 '$.city')都区分大小写,且不支持变量拼接——'$.' + @key 在 JSON_VALUE 里无效,必须用动态 SQL 组装整条语句。这不是性能问题,而是语法刚性限制。


















