JSON_VALUE仅提取标量值,嵌套对象或数组返回NULL;路径须严格匹配大小写、点号分隔、数组索引从0开始;应配合TRY_CAST、ISNULL等处理类型与空值,并优先用OPENJSON或计算列优化性能。

JSON_VALUE只能取标量值,嵌套对象或数组一律返回NULL
这不是bug,是设计使然。JSON_VALUE的语义就是“提取一个字符串、数字、布尔或null”,它不处理结构。哪怕你传入'$.address',而address在原始JSON里是个对象(如{"city":"Beijing"}),结果也是NULL。
常见误判场景:
- 看到JSON里有
"items": [{"id": 1}, {"id": 2}],就写JSON_VALUE(data, '$.items')→ 返回NULL - 路径写成
'$.user.profile["name"]'(带方括号)→ SQL Server不支持,静默返回NULL - 字段名大小写不一致,比如JSON里是
"Name"却查'$.name'→NULL
验证方法:先用JSON_QUERY(data, '$.address')确认该路径确实存在且是对象;若要取其中的city,再换用JSON_VALUE(data, '$.address.city')。
深层嵌套和数组索引必须严格匹配路径语法
SQL Server的JSON路径是大小写敏感、点号分隔、数组索引从0开始的纯文本表达式,不支持通配符或模糊匹配。
以这个JSON为例:
{ "order": { "items": [ { "sku": "A100", "qty": 2 }, { "sku": "B200", "qty": 1 } ] } }
以下操作才有效:
-
JSON_VALUE(doc, '$.order.items[0].sku')→"A100" -
JSON_VALUE(doc, '$.order.items[1].qty')→"1"(注意返回仍是varchar) -
JSON_VALUE(doc, '$.order.items[0]')→NULL(因为[0]指向对象,不是标量) -
JSON_VALUE(doc, '$.Order.items[0].sku')→NULL(Order首字母大写,但原始键是order)
开发阶段建议加ERROR ON ERROR:写成JSON_VALUE(doc, '$.order.items[0].price' ERROR ON ERROR),路径错或字段不存在时直接报错,避免被NULL掩盖问题。
类型转换和空值传播必须显式处理
JSON_VALUE永远返回varchar(4000),哪怕原始JSON里是123或true。不做转换就参与计算,大概率出错。
典型陷阱:
- 想把
JSON_VALUE(doc, '$.count')当整数用:WHERE JSON_VALUE(doc, '$.count') > 10→ 隐式转换失败,报错 - 字段可能为空或缺失:
JSON_VALUE(doc, '$.status')为NULL,后续ISNULL(..., 'active')没包住,导致整个表达式为NULL - 数值精度丢失:JSON里是
"price": 199.99,用CAST(... AS int)直接截断成199
安全写法:
- 数值字段:用
TRY_CAST(JSON_VALUE(doc, '$.amount') AS decimal(10,2)) - 布尔字段:用
CASE WHEN JSON_VALUE(doc, '$.is_valid') = 'true' THEN 1 ELSE 0 END - 必填字段兜底:
ISNULL(NULLIF(RTRIM(JSON_VALUE(doc, '$.code')), ''), 'UNKNOWN')
视图或大表中高频调用JSON_VALUE会显著拖慢查询
每次调用JSON_VALUE都会对同一行的JSON字符串重新解析一次。如果一个视图定义里写了6个JSON_VALUE字段,扫描10万行,等于执行了60万次JSON解析 —— 不是10万次解析复用6次。
优化思路:
- 优先考虑用
OPENJSON一次性展开,尤其当你要取3个以上字段时:CROSS APPLY OPENJSON(doc) WITH (id int '$.id', name nvarchar(50) '$.name', ts datetime2 '$.ts') - 对高频查询字段,建计算列并索引:
ALTER TABLE Orders ADD status AS JSON_VALUE(doc, '$.status'),再在status上建非聚集索引 - 避免在
WHERE里对JSON_VALUE结果做函数运算,例如UPPER(JSON_VALUE(...)) = 'PAID'→ 无法走索引,应提前物化
最易被忽略的一点:ISJSON(doc) = 0的行,JSON_VALUE也返回NULL,但你无法从结果区分是“真没这个字段”还是“整条JSON格式损坏”。生产环境必须前置过滤或打标记。











