MySQL CHECK 约束怎样限制 JSON 字段结构
如果 JSON 字段只要“能解析”还不够,还需要限制它必须是对象、包含指定键,并且键值符合类型与取值范围,可以把这些规则写进 MySQL 的 CHECK 约束。核心组合是:JSON_CONTAINS_PATH() 检查路径是否存在,JSON_TYPE() 检查 JSON 类型,JSON_EXTRACT() 取出值后再做范围或枚举判断。
官方地址:https://www.mysql.com/
- JSON 列本身负责文档合法性,
CHECK负责业务结构和取值约束。 - 必需字段先用
JSON_CONTAINS_PATH(payload, 'all', ...)判断,再检查字段类型。 - JSON
null、缺失路径和 SQLNULL不是一回事;若字段必须存在,建议同时使用NOT NULL与路径检查。 - MySQL 的 CHECK 表达式必须使用允许的确定性表达式,不能放子查询、变量或存储函数。
先把 JSON 结构拆成四类条件
以订单事件为例,约束目标可以明确成四层:顶层必须是 JSON 对象;必须有 kind 和 version;kind 必须是字符串且只能取 invoice 或 refund;version 必须是整数并落在 1 到 3 之间。把自然语言拆开后,每一层都能对应一个可读的 SQL 表达式。

| 业务规则 | 推荐表达式 | 判断结果 |
|---|---|---|
| 顶层必须是对象 | JSON_TYPE(payload) = 'OBJECT' | 排除数组、字符串和标量文档 |
| 字段必须存在 | JSON_CONTAINS_PATH(payload, 'all', '$.kind', '$.version') | 所有路径都存在才返回 1 |
| 字段必须是字符串 | JSON_TYPE(JSON_EXTRACT(payload, '$.kind')) = 'STRING' | 避免把数字或布尔值当作文本 |
| 字段只能取有限值 | JSON_UNQUOTE(JSON_EXTRACT(payload, '$.kind')) IN (...) | 实现简单的枚举约束 |
创建带命名 CHECK 的 JSON 表
下面的表只接受三种版本范围内的订单事件。payload 使用原生 JSON 类型,因此 MySQL 先负责 JSON 文档的语法合法性;命名的 CHECK 再负责本文关心的结构规则。
CREATE TABLE order_event (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
payload JSON NOT NULL,
PRIMARY KEY (id),
CONSTRAINT chk_order_event_shape CHECK (
-- 顶层必须是对象,避免把数组或标量当成事件文档。
JSON_TYPE(payload) = 'OBJECT'
-- kind 与 version 都必须出现,缺失路径返回 0。
AND JSON_CONTAINS_PATH(payload, 'all', '$.kind', '$.version')
-- kind 先判类型,再限制为两个业务枚举值。
AND JSON_TYPE(JSON_EXTRACT(payload, '$.kind')) = 'STRING'
AND JSON_UNQUOTE(JSON_EXTRACT(payload, '$.kind')) IN ('invoice', 'refund')
-- version 必须是 JSON 整数,并限制在当前支持的版本区间。
AND JSON_TYPE(JSON_EXTRACT(payload, '$.version')) IN ('INTEGER', 'UNSIGNED INTEGER')
AND CAST(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.version')) AS UNSIGNED)
BETWEEN 1 AND 3
)
);
这里把 JSON_CONTAINS_PATH(..., 'all', ...) 放在类型和取值条件之前,是为了让“字段缺失”成为一个清晰的失败条件。JSON_EXTRACT() 返回的是 JSON 值,字符串字段需要再用 JSON_UNQUOTE() 去掉 JSON 字符串的引号后,才能和 SQL 字符串枚举比较。
示例使用了确定性的内置 JSON 函数和转换,没有引入变量、子查询或存储函数。MySQL 8.4 文档规定,CHECK 表达式必须遵守这些限制;约束名还需要在同一 schema 的 CHECK 命名空间内保持唯一。
按四种失败类型检查数据
不要只准备一条成功样本。结构约束最有价值的地方,是把常见的脏数据在写入边界挡住。下面四组 INSERT 分别对应合法对象、缺少字段、类型错误和枚举错误。
-- 合法样本:对象、必需字段、类型和取值都满足约束。
INSERT INTO order_event (payload)
VALUES ('{"kind":"invoice","version":1}');
-- 缺少 version:JSON 文档合法,但必需路径检查返回 0。
INSERT INTO order_event (payload)
VALUES ('{"kind":"invoice"}');
-- version 是字符串:字段存在,但 JSON_TYPE 不是整数类型。
INSERT INTO order_event (payload)
VALUES ('{"kind":"invoice","version":"1"}');
-- kind 不在枚举范围:类型正确,但业务取值不被接受。
INSERT INTO order_event (payload)
VALUES ('{"kind":"shipment","version":1}');
第二、三、四条数据会触发 CHECK 约束错误;它们不是“JSON 格式错误”,而是 JSON 已经能被解析,只是不符合表定义的业务结构。这个区分很重要:前者应排查输入序列化,后者应排查契约或调用方版本。

为什么必须同时理解 SQL NULL、JSON null 和缺失路径
MySQL CHECK 条件只要得到 TRUE 或 UNKNOWN 就可以通过,得到 FALSE 才会拒绝。因此,设计“字段必须存在”时不能只依赖某个可能返回 SQL NULL 的比较。本文的表把整列声明为 JSON NOT NULL,再用 JSON_CONTAINS_PATH(..., 'all', ...) 把缺失字段转换成明确的 0。
还要分清三种情况:
- SQL
NULL:整列没有值,会受到NOT NULL约束。 - JSON
null:文档里存在一个 JSON null 值,路径存在,但它不是字符串或整数。 - 缺失路径:对象中根本没有这个键,
JSON_CONTAINS_PATH会返回 0。
-- 用查询把缺失、JSON null 和正常值放在一起观察。
SELECT
JSON_CONTAINS_PATH('{"kind":null}', 'all', '$.kind') AS has_null_kind,
JSON_CONTAINS_PATH('{}', 'all', '$.kind') AS has_missing_kind,
JSON_TYPE(JSON_EXTRACT('{"kind":"invoice"}', '$.kind')) AS normal_kind_type;
如果业务允许某个字段缺失,就不要把它放进 'all' 的必需路径列表;如果允许 JSON null,也要在类型条件中明确写出相应分支,而不是依赖 UNKNOWN 带来的偶然放行。
更新、REPLACE 和 IGNORE 也会触发约束
CHECK 不是只在首次 INSERT 时检查。MySQL 会在 INSERT、UPDATE、REPLACE、LOAD DATA 和 LOAD XML 等写入场景评估它。应用只验证创建请求而不验证更新请求,仍然可能在更新时收到约束错误。
-- 先取得一条合法记录,再用 UPDATE 演示同一条规则仍然生效。
UPDATE order_event
SET payload = '{"kind":"shipment","version":1}'
WHERE id = 1;
-- IGNORE 会把违反 CHECK 的行跳过并产生警告,不应被误当成成功写入。
INSERT IGNORE INTO order_event (payload)
VALUES ('{"kind":"invoice"}');
在事务型表中,正常写入失败时应由调用方处理错误并决定是否回滚;使用 IGNORE 时则必须读取警告或检查受影响行数,否则可能出现“接口返回成功,但坏行没有落库”的隐蔽问题。
部署后怎样确认约束真的存在
上线前后都可以从数据库元数据查看最终定义。SHOW CREATE TABLE 适合人工阅读完整建表语句,INFORMATION_SCHEMA.CHECK_CONSTRAINTS 适合被迁移检查脚本读取。它们用于确认部署结果,不替代应用层的业务测试。
-- 查看数据库最终保存的约束名称和表达式。 SHOW CREATE TABLE order_event; -- 按 schema 查询 CHECK 表达式,便于迁移脚本对比目标状态。 SELECT CONSTRAINT_SCHEMA, TABLE_NAME, CONSTRAINT_NAME, CHECK_CLAUSE FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS WHERE TABLE_NAME = 'order_event';
| 需求 | 适合的做法 | 注意点 |
|---|---|---|
| 必需键、简单类型、有限枚举 | JSON + CHECK | 表达式要保持短小、可读、确定性 |
| 多层数组、条件分支、复杂错误报告 | 应用层校验或 JSON Schema | 不要把整套 schema 压成一条难维护的 CHECK |
| 高频筛选的稳定字段 | 生成列或表达式索引 | 约束负责正确性,索引负责访问路径 |
总结:让数据库守住最小结构契约
MySQL CHECK 约束限制 JSON 字段结构时,推荐按“对象类型 → 必需路径 → JSON 类型 → 枚举或范围”的顺序编写。原生 JSON 类型解决文档语法,CHECK 解决表级业务边界;NOT NULL 和明确的路径判断则避免 SQL NULL、JSON null 与缺失字段混在一起。
常见追问:CHECK 能不能直接验证完整 JSON Schema? 可以在 MySQL 支持的 JSON Schema 函数和确定性约束范围内评估,但复杂 schema 会明显降低约束的可读性与迁移维护性;简单结构适合 CHECK,复杂校验应在应用层或专门的 schema 流程中保留更清晰的错误信息。
常见追问:为什么 JSON 列还需要 CHECK? JSON 列主要保证文档合法,并不自动知道你的业务键、类型和枚举要求;CHECK 正好把最小业务契约下沉到数据库写入边界。
常见追问:只写 JSON_TYPE 是否足够? 不够。类型正确不代表路径存在、值在允许范围内,也不代表多个字段之间满足业务关系。
MySQL table_io_waits_summary_by_table 怎样看表级 I/O 等待
- 上一篇
- MySQL table_io_waits_summary_by_table 怎样看表级 I/O 等待
- 下一篇
- MySQL 字符集转换怎样定位非法字节
-
- 数据库 · MySQL | 2分钟前 |
- MySQL 直方图怎样删除过期统计信息
- 112浏览 收藏
-
- 数据库 · MySQL | 4分钟前 |
- MySQL 复制过滤怎样确认表规则已加载
- 486浏览 收藏
-
- 数据库 · MySQL | 5分钟前 | MySQL · mysql 几何有效性 ST_IsValid GIS
- MySQL ST_IsValid 怎样判断几何对象是否有效
- 500浏览 收藏
-
- 数据库 · MySQL | 12分钟前 |
- MySQL table_io_waits_summary_by_table 怎样看表级 I/O 等待
- 405浏览 收藏
-
- 数据库 · MySQL | 32分钟前 |
- MySQL InnoDB 怎样查看锁等待事务
- 171浏览 收藏
-
- 数据库 · MySQL | 35分钟前 | MySQL · 字符串处理 · mysql 正则表达式 regexp_substr occurrence
- MySQL REGEXP_SUBSTR 怎样提取指定出现次数的文本
- 238浏览 收藏
-
- 数据库 · MySQL | 36分钟前 | mysql 空间数据 LineString ST_IsSimple 自相交 GIS
- MySQL ST_IsSimple 怎样判断 LineString 是否自相交
- 262浏览 收藏
-
- 数据库 · MySQL | 36分钟前 |
- MySQL CHECK 约束怎样处理 NULL 值
- 347浏览 收藏
-
- 数据库 · MySQL | 6小时前 | MySQL · 数据库 · mysql 请求来源 query_attributes mysql_query_attribute_string 查询属性
- 用查询属性把页面或请求来源传给当前 SQL 并在服务器端读取
- 501浏览 收藏
-
- 数据库 · MySQL | 6小时前 | MySQL · JSON · 执行计划 · 查询优化 · mysql explain json_extract 执行计划 用户变量 FORMAT=JSON INTO
- 使用 EXPLAIN FORMAT=JSON INTO 保存计划并交给 JSON 函数读取
- 494浏览 收藏
-
- 数据库 · MySQL | 6小时前 |
- 比较 sys.format_bytes 与内置 FORMAT_BYTES 的单位和迁移注意事项
- 462浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 896次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 901次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 985次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 972次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 798次使用
-
- 接口返回 200 但前端仍报错怎么办:从响应格式到跨域一步步排查
- 2026-06-14 332浏览
-
- MySQL 明明加了索引,为什么查询还是很慢?先查这 6 个点
- 2026-06-27 374浏览
-
- golang MySQL实现对数据库表存储获取操作示例
- 2022-12-22 499浏览
-
- golang 基于 mysql 简单实现分布式读写锁
- 2023-01-07 384浏览
-
- 详解如何利用GORM实现MySQL事务
- 2023-01-07 184浏览

