check约束仅从mysql 8.0.16起真正生效,此前版本(含8.0.0–8.0.15及5.7)仅解析语法但不校验;需运行select version()确认真实版本≥8.0.16,列级约束限当前列,表级可跨列,默认enforced,表达式禁用子查询与非确定函数。

CHECK 约束在 MySQL 8.0 中不是“从 8.0 开始支持”,而是仅从 8.0.16 版本起才真正生效。低于该版本(包括 8.0.0–8.0.15)的 CHECK 语句会被解析但直接丢弃,不校验、不报错、不写入元数据。
CHECK 约束实际生效的前提:确认 MySQL 版本 ≥ 8.0.16
- 运行
SELECT VERSION();查看真实版本号,不能只看大版本 - 若返回类似
8.0.15或5.7.32,即使语法能执行,CHECK也形同虚设 - 常见误判场景:Docker 镜像标签写
mysql:8.0,但拉取到的是旧版;或云数据库控制台显示“MySQL 8.0”,底层实际是 8.0.14
列级 vs 表级 CHECK:表达式引用范围不同
列级约束必须写在列定义内,只能引用当前列
c1 INT CHECK(c1 > 0)✅c1 INT CHECK(c1 > c2)❌(c2尚未定义,且不允许跨列)表级约束写在所有列定义之后,可引用任意已有列,甚至多个列组合
CHECK(c1 > c2 AND c2 > c3)✅CHECK(status IN ('0', '1'))✅(哪怕status是最后一列)约束名可选,但强烈建议显式命名,否则 MySQL 自动生成如
t1_chk_1这类难维护的名字
ENFORCED 和 NOT ENFORCED:控制是否立即校验
- 默认为
ENFORCED,插入/更新时违反即报错:ERROR 3819 (HY000): Check constraint 'xxx' is violated. - 指定
NOT ENFORCED后,约束会存入元数据,但不触发检查ALTER TABLE t ADD CONSTRAINT chk_age CHECK(age >= 18) NOT ENFORCED; - 适用场景:迁移旧数据时临时禁用校验,后续再
ALTER TABLE ... ENFORCED激活(注意:MySQL 不支持直接修改已存在约束的 enforced 状态,需先删后加)
容易被忽略的限制和坑点
-
CHECK表达式中禁止使用子查询、存储函数、用户变量、NOW()等非确定性函数,否则建表失败 -
NULL值不会触发违反:表达式结果为UNKNOWN(而非FALSE)时,约束通过
例如CHECK(score > 60),当score IS NULL时仍允许插入 - 所有约束(
PRIMARY KEY、UNIQUE、CHECK等)共享同一命名空间,同 schema 内不能重名CONSTRAINT chk_status CHECK(...)和CONSTRAINT chk_status UNIQUE(...)会冲突
真正的数据验证发生在 SQL 层写入路径上,不依赖存储引擎。只要版本达标、语法合规、表达式确定,它就会在语句执行前拦截非法值——这点比触发器更轻量,也比应用层校验更可靠。但别忘了:它不处理 NULL,也不支持动态逻辑,复杂规则仍得靠触发器或业务代码兜底。











