Oracle 21c JSON列需用触发器验证:先用IS JSON = 0判语法合法性,再用JSON_VALUE(... ERROR ON ERROR)校验业务规则;自动补全用JSON_MERGE_PATCH;嵌套数组校验宜移至应用层。

Oracle 21c 的 JSON 数据类型本身不支持列级约束(比如 CHECK (json_data IS JSON) 在 21c 中虽可用,但无法捕获结构或业务逻辑错误),所以必须靠触发器补位。直接在 INSERT 或 UPDATE 时验证,比事后查错更可靠。
触发器里怎么判断字段是合法 JSON?用 IS JSON 而不是 JSON_EXISTS
IS JSON 是唯一能确认字符串是否为语法合法 JSON 的内置条件,它返回 1 或 0(不是布尔值,注意类型)。JSON_EXISTS 前提是输入已为合法 JSON,若传入乱码会直接报错(如 ORA-40442: JSON syntax error),导致触发器中断,反而绕过验证。
- 必须写成
IF json_col IS JSON = 0 THEN RAISE_APPLICATION_ERROR(...); END IF; - 不能写成
IF NOT JSON_EXISTS(json_col, '$') THEN ...—— 这会在非法 JSON 上抛异常,无法进入THEN分支 - 如果字段允许为
NULL,需额外判断:json_col IS NULL OR json_col IS JSON = 1
业务规则验证得用 JSON_VALUE + 异常捕获,别硬套 CHECK
比如要求 $.status 必须是 'active' 或 'inactive',且 $.created_at 必须是 ISO 格式时间。这类逻辑无法用单个 SQL 条件表达,必须在 PL/SQL 块中调用 JSON_VALUE 并捕获 VALUE_ERROR 或检查返回值:
-
JSON_VALUE(json_col, '$.status' RETURNING VARCHAR2(20))若路径不存在会返回NULL,不是报错;若想强制存在,加ERROR ON ERROR - 写法示例:
l_status := JSON_VALUE(json_col, '$.status' ERROR ON ERROR); IF l_status NOT IN ('active', 'inactive') THEN ... - 时间字段验证建议先用
JSON_VALUE(... RETURNING TIMESTAMP),成功则说明格式可转,再加业务逻辑判断(如不能晚于当前时间)
触发器里修改 JSON 内容?别碰 JSON_TRANSFORM,用 JSON_MERGE_PATCH 更安全
有些场景需要“自动补全缺失字段”,比如插入时没给 $.version 就默认设为 1。这时不要在触发器里用 JSON_TRANSFORM(它语法复杂、易写错路径,且对空对象处理不直观),改用 JSON_MERGE_PATCH:
-
NEW.json_data := JSON_MERGE_PATCH(NEW.json_data, '{"version": 1}');—— 自动合并,不覆盖已有字段 - 如果原值是
NULL,JSON_MERGE_PATCH(NULL, '{...}')返回{...},行为可预期 - 避免用字符串拼接(如
'{"version":1,' || json_col || '}'),极易因引号或转义出错 - 注意:触发器中赋值必须用
:NEW.json_data(行级触发器),且表定义中该列为JSON类型,否则隐式转换可能失败
真正难的是嵌套数组的业务校验——比如 $.items[*].price 每一项都必须 > 0。这必须用 JSON_TABLE 展开后查,但触发器里执行 DML 式查询开销大,还可能引发 mutating table 错误。这种逻辑更适合移到应用层或用物化视图+约束组合实现,别硬塞进触发器。


















