OPENJSON函数需显式指定WITH子句或默认架构,否则仅返回name/type/value三列;WITH中类型不匹配会导致NULL且不报错;嵌套结构须配合CROSS APPLY二次解析;仅支持SQL Server 2016+。

OPENJSON函数的基本用法和必需参数
OPENJSON 是 SQL Server 2016+ 提供的原生 JSON 解析函数,它把 JSON 文本转成行集(类似临时表),但**必须显式指定 WITH 子句或使用默认架构**,否则只返回键值对的 name/type/value 三列,无法直接映射业务字段。
常见错误是直接写 SELECT * FROM OPENJSON(@json),结果看到一堆 key、value、type,根本不是你想要的列。
- 如果 JSON 是对象(如
{"id":1,"name":"Alice"}),用WITH显式声明列名、类型和路径 - 如果 JSON 是数组(如
[{"id":1},{"id":2}]),OPENJSON默认按数组元素展开为多行,无需额外处理 -
path参数可选,用于定位嵌套对象,例如'$.data.items';不填则从根开始
如何正确声明 WITH 子句映射字段类型
WITH 子句决定输出列的结构和类型,**类型不匹配会导致值为 NULL,且不会报错**——这是最常被忽略的坑。
比如 JSON 中 "age": "25"(字符串)但你在 WITH 里写 age INT '$.age',结果就是 NULL;同理,日期字段必须用 datetime2 或 date,且 JSON 里得是 ISO 格式("2023-01-01"),否则也转不出来。
- 字符串用
nvarchar(50),别漏了n前缀,否则可能乱码 - 布尔值对应
bit,JSON 中必须是小写true/false - 嵌套对象用
json类型保留原始 JSON 片段,后续可再用OPENJSON解析 - 路径支持通配符,如
'$.tags[*]'可展开数组,但需配合AS JSON使用
SELECT id, name, active FROM OPENJSON(@json) WITH ( id INT '$.id', name NVARCHAR(50) '$.name', active BIT '$.active' );
处理嵌套 JSON 和数组的典型场景
真实 JSON 往往有层级,比如用户信息带地址数组:{"user_id":1,"addresses":[{"city":"Beijing"},{"city":"Shanghai"}]}。这时候不能只靠一层 OPENJSON 拿全数据。
标准做法是:先主对象解析出用户字段,再用 CROSS APPLY 对数组字段二次解析。**漏掉 CROSS APPLY 就只能拿到整个数组的 JSON 字符串,没法拆成多行地址记录。**
- 主查询用
OPENJSON解析顶层字段,其中数组字段声明为AS JSON - 用
CROSS APPLY OPENJSON(...)对该字段再次解析,得到子行集 - 避免在
WITH中直接写'$.addresses.city'—— 这样只会取第一个元素,且无法处理多条 - SQL Server 不支持 JSON 路径中的动态索引(如
$[0]),所以必须依赖APPLY展开
SELECT u.user_id, a.city FROM OPENJSON(@json) WITH (user_id INT, addresses NVARCHAR(MAX) AS JSON) AS u CROSS APPLY OPENJSON(u.addresses) WITH (city NVARCHAR(50) '$.city') AS a;
性能与兼容性注意事项
OPENJSON 是标量函数,但底层会触发 JSON 解析开销。**反复解析同一段 JSON(比如在 JOIN 或 WHERE 中多次调用)会显著拖慢查询。**
另外,它仅在 SQL Server 2016 及以上版本可用,Azure SQL Database 全支持,但 SQL Server 2014 或更早版本完全不可用——别指望用 sp_executesql + 动态 SQL 绕过。
- 大 JSON 文本(>2MB)可能导致内存压力,建议提前用
LEN(@json)控制输入大小 - 没有索引支持 JSON 字段查询,WHERE 条件中对解析后列过滤是高效的,但对原始 JSON 字符串 LIKE 搜索很慢
- 如果 JSON 结构不稳定(字段时有时无),
WITH中未出现的字段直接丢弃,不会报错也不会补 NULL —— 需要靠应用层或前置校验保障格式
真正麻烦的是混合类型字段(比如 "score": 95 有时变成 "score": "N/A"),SQL Server 不允许同一列混用 INT 和 nvarchar,只能统一声明为字符串再手工转换,这里容易埋运行时异常。


















