OPENJSON解析嵌套数组必须显式指定路径(如$.items[*].id),默认仅展开第一层;多层嵌套需用CROSS APPLY链式调用;类型不匹配、路径错误或空格大小写不符会导致NULL;深层嵌套应预处理或物化结果以提升性能。

OPENJSON解析嵌套数组时必须显式指定路径
SQL Server的OPENJSON默认只展开第一层,遇到"items": [{"id":1},{"id":2}]这类结构,不加路径就只能看到items字段本身,拿不到内部对象。必须用WITH子句配合$.items[0].id或更通用的$.items[*].id才能触达。
常见错误是直接写SELECT * FROM OPENJSON(@json) WITH (id INT '$.items.id')——这会返回NULL,因为$.items.id试图从数组对象取id,而数组没有id属性。
- 对数组元素取值,路径必须带
[*](如$.data.users[*].name) - 如果嵌套层级深(如
$.response.data.list[*].details.fields[*].value),路径里可以连用多个[*] -
[0]能取第一个,但不可靠;生产环境一律用[*]配合CROSS APPLY展开
多层嵌套需用CROSS APPLY链式展开
单个OPENJSON只能展开一层。要解析{"orders":[{"items":[{"sku":"A"},{"sku":"B"}]}]}这种两层嵌套,得先展开orders,再对每个order的items字段再次调用OPENJSON。
关键不是“能不能”,而是“怎么串”。靠CROSS APPLY把上一层结果作为下一层的输入源,形成流水线:
SELECT ord.order_id, itm.sku FROM OPENJSON(@json, '$.orders') WITH (order_id INT '$.id') AS ord CROSS APPLY OPENJSON(ord.value, '$.items') WITH (sku NVARCHAR(10) '$.sku') AS itm
-
OPENJSON(@json, '$.orders')定位到数组,WITH提取每项的id -
ord.value是每个order对象的原始JSON字符串,传给下一级OPENJSON - 第二层
OPENJSON(ord.value, '$.items')才真正展开items数组
类型不匹配导致字段为NULL的典型原因
即使路径写对了,字段仍为NULL,八成是类型声明和实际JSON值对不上。比如JSON里"count": "5"(字符串),但WITH里写count INT '$.count',SQL Server不会自动转换,直接给NULL。
- 字符串数字(
"123")要转成INT,得先用NVARCHAR接住,再CAST或TRY_CAST - 布尔值
true/false在SQL Server里对应BIT,但JSON里没引号,写active BIT '$.active'才有效 - 日期字段如
"2024-03-15",用DATE类型能直接映射;但带时间的"2024-03-15T10:30:00"得用DATETIME2 - 不确定类型时,先用
NVARCHAR(MAX)全接进来,再人工检查再调整
性能陷阱:深层嵌套+大数据量时别硬套OPENJSON
每多一层CROSS APPLY OPENJSON,就多一次JSON解析开销。如果原始JSON有1000个orders,每个含50个items,那就要调用50000次OPENJSON——CPU和内存压力会明显上升。
- 提前用
ISJSON(@json) = 0过滤非法JSON,避免解析失败拖慢查询 - 嵌套超过3层、单条JSON超1MB时,考虑预处理:用.NET/Python先把JSON扁平化成关系表,再导入SQL Server
- 高频查询场景,把解析结果存到物理表并建索引,而不是每次实时
OPENJSON
最易被忽略的是路径里的空格和大小写——JSON键名"ItemID"写成$.itemID就完全匹配不上,调试时盯紧原始JSON的精确拼写。











