所有数据库解析JSON前必须校验结构合法性,否则解析失败即硬错误;SQL Server需WITH子句映射类型,PostgreSQL要求jsonb_to_record完全匹配,MySQL需显式转换类型,Oracle须用JSON_VALID前置校验。

SQL Server里用OPENJSON解析JSON参数必须加WITH子句
不加WITH直接SELECT * FROM OPENJSON(@json)只能拿到键名和原始值,没法映射成带类型、可参与计算的列。尤其当JSON里有嵌套对象或数组时,缺WITH会导致后续JOIN或WHERE失效。
实操建议:
-
WITH里每个字段必须显式声明类型,比如name NVARCHAR(50),不能只写name - 路径表达式要用
'$.field'格式,注意单引号包裹,双引号会报错 - 如果JSON字段可能为
null,类型后面加NULL(如status TINYINT NULL),否则遇到空值直接中断解析 - 嵌套对象用
JSON_QUERY('$.items')提取字符串,再套一层OPENJSON处理,别试图在一级WITH里展开多层
PostgreSQL中jsonb_to_record()要求输入严格匹配字段定义
jsonb_to_record()不是“尽力而为”,而是“完全匹配”。传入JSON里多一个字段、少一个字段、类型对不上,都会抛ERROR: record type does not match supplied data。
常见错误现象:
- 前端传了
{"user_id": "123", "tags": ["a","b"]},但函数定义里写了tags TEXT[]——看着一样,实际"123"是字符串,123才是整数,类型不匹配就崩 - 用
jsonb_populate_record()时没给默认值,JSON缺字段就报错,而不是填NULL - 字段名大小写敏感:JSON里是
userId,函数定义写userid,直接不识别
安全做法是先用jsonb_typeof()校验关键字段类型,再进主逻辑;或者改用jsonb_path_query()配合COALESCE做兜底。
MySQL 8.0+的JSON_EXTRACT()返回的是JSON字符串,不是原生类型
JSON_EXTRACT(@json, '$.price')结果是"199.00"(带双引号),不是数值199.00。直接拿它参与WHERE price > 100会隐式转成字符串比较,导致逻辑错乱。
正确解法只有两个:
- 用
CAST(JSON_EXTRACT(@json, '$.price') AS DECIMAL(10,2))显式转类型 - 用
JSON_UNQUOTE(JSON_EXTRACT(@json, '$.price'))去掉引号后再转,否则CAST对带引号字符串无效 - 数组字段如
$.tags不能直接IN,得先JSON_TABLE()展开成行集 - 别信
->操作符能省事——@json->'$.price'和JSON_EXTRACT()行为一致,同样要处理引号
Oracle存储过程中解析JSON需提前检查JSON_VALID(),否则ORA-40441必现
Oracle 12c+虽支持JSON_VALUE(),但只要输入不是合法JSON,哪怕只是多一个逗号、少一个括号,就会立刻抛ORA-40441: JSON syntax error,且无法被EXCEPTION块捕获——它属于编译期错误,不是运行时异常。
所以必须前置校验:
- 调用
JSON_VALID(@json) = 'TRUE',返回FALSE就直接RETURN或抛自定义异常 -
JSON_VALUE()的路径参数必须是字符串字面量,不能拼接变量,否则编译失败 - 取嵌套值如
$.data.items[0].name,索引越界不会报错,而是返回NULL,这点和PostgreSQL不同 - 如果JSON含Unicode转义(如
\u4f60),确保数据库字符集是AL32UTF8,否则解析后变乱码
真正难的不是语法怎么写,而是所有数据库都把“JSON解析失败”当作硬错误而非可恢复异常——这意味着你得在进存储过程第一行就做完结构校验,而不是等执行到中间才崩。漏掉这步,线上就等着收告警。


















