openjson 默认仅解析一层,无法自动递归展开深层嵌套对象或数组;必须通过as json保留结构并嵌套调用openjson,或结合json_value按需提取。

不能直接用单层 OPENJSON 展开多层嵌套对象,必须分步解析或嵌套调用 OPENJSON。
为什么单次 OPENJSON 无法解析深层嵌套
SQL Server 的 OPENJSON 默认只展开**一层**:对 JSON 对象,它把顶层属性转成行;对数组,它把每个元素转成一行。它不会递归解析子对象或子数组里的字段。
比如 '{ "user": { "profile": { "name": "Alice" } } }',OPENJSON(@json) 只返回一行,value 列是 {"profile": {"name": "Alice"}} 这个字符串,不是结构化数据。
- 错误现象:
SELECT * FROM OPENJSON(@json, '$.user.profile') WITH (name NVARCHAR(50) '$.name')返回空——因为$.user.profile指向的是一个对象,不是数组,而OPENJSON的路径参数要求目标是数组才能逐项展开 - 正确前提:路径必须指向一个 JSON 数组(如
'$.skills'),或配合WITH显式声明对象字段(但仅限一级) - 根本限制:
WITH子句中不支持嵌套路径的自动展开,'$.user.profile.name'是合法路径,但只能用于JSON_VALUE,不能在WITH中“穿透”两层对象后映射到列
用嵌套 OPENJSON 解析两级以上对象
核心思路:先用外层 OPENJSON 提取嵌套对象(作为字符串),再用内层 OPENJSON 解析该字符串。
示例:解析 { "order": { "id": 1001, "items": [ {"sku": "A1", "qty": 2}, {"sku": "B2", "qty": 1} ] } }
DECLARE @json NVARCHAR(MAX) = N'{ "order": { "id": 1001, "items": [ {"sku": "A1", "qty": 2}, {"sku": "B2", "qty": 1} ] } }';
<p>SELECT
o.id,
i.sku,
i.qty
FROM OPENJSON(@json, '$.order')
WITH (
id INT '$.id',
items NVARCHAR(MAX) AS JSON -- 注意:必须标为 AS JSON,否则会被当字符串截断
) AS o
CROSS APPLY OPENJSON(o.items)
WITH (
sku VARCHAR(10) '$.sku',
qty INT '$.qty'
) AS i;</p>
-
items NVARCHAR(MAX) AS JSON是关键:不加AS JSON,SQL Server 会把整个数组当成普通字符串(可能被截断),加了才保留完整 JSON 结构供下层解析 -
CROSS APPLY是必须的:它让内层OPENJSON基于每行外层结果执行,实现“每订单展开其 items” - 性能影响:嵌套越深、数组越大,解析开销越明显;避免在大表上对每行都做多次
OPENJSON
解析含对象字段的数组(如地址列表)
常见场景:JSON 中有个 "addresses": [ { "city": "Beijing", "zip": "100000" }, ... ],需要提取 city 和 zip。
不能写 city NVARCHAR(50) '$.addresses.city' —— 这是无效路径,因为 addresses 是数组,不是对象。
- 正确做法:先定位到数组
'$.addresses',再在WITH中用点号访问数组元素内的字段 -
WITH中的路径是相对于数组每个元素的,所以'$.city'就表示“当前数组项里的 city 字段” - 若数组项里还有嵌套对象(如
"geo": { "lat": 39.9, "lng": 116.4 }),仍需用AS JSON+ 再次OPENJSON拆解
示例:
SELECT
a.city,
a.zip,
g.lat,
g.lng
FROM OPENJSON(@json, '$.addresses')
WITH (
city NVARCHAR(50) '$.city',
zip NVARCHAR(20) '$.zip',
geo NVARCHAR(MAX) AS JSON
) AS a
CROSS APPLY OPENJSON(a.geo)
WITH (
lat DECIMAL(9,6) '$.lat',
lng DECIMAL(9,6) '$.lng'
) AS g;
JSON_VALUE 和 OPENJSON 的分工边界
简单取值用 JSON_VALUE,结构化展开用 OPENJSON——这个原则在嵌套场景下依然成立,但容易误用。
-
JSON_VALUE(@json, '$.order.id')快且安全,适合单值提取 -
JSON_VALUE(@json, '$.order.items[0].sku')能取数组首项,但无法动态遍历全部项;一旦要“所有 items”,必须切到OPENJSON - 混合使用常见:外层用
JSON_VALUE提取主键或标识字段,内层用OPENJSON展开变长数组——减少嵌套层级,提升可读性 - 容易踩的坑:在存储过程中拼接 JSON 路径时,变量未转义导致路径语法错误,例如
'$.' + @key中@key含点号或括号,应改用CONCAT('$.', QUOTENAME(@key, '"'))
最复杂的嵌套往往不是技术做不到,而是路径写错、AS JSON 忘加、或误以为 WITH 能自动递归——盯住每一层的数据类型(对象?数组?字符串?),再决定用 JSON_VALUE、OPENJSON 还是嵌套 OPENJSON。











