check约束在mysql 8.0.16及以上版本才真正生效;此前版本仅解析语法但忽略校验,插入非法值不报错,show create table中也看不到约束定义。

CHECK 约束在 MySQL 中只有从 8.0.16 版本起才真正生效;低于该版本(包括 8.0.15 及所有 5.7/5.6 等旧版),CHECK 语法会被解析但完全忽略,插入违规数据不会报错。
确认你的 MySQL 版本是否真正支持 CHECK
执行 SELECT VERSION();,结果必须是 8.0.16 或更高(如 8.0.33、9.6.0)。注意:8.0.0 到 8.0.15 均不强制执行。
- 若版本达标,
ENFORCED列在information_schema.TABLE_CONSTRAINTS中应为YES - 若为
NO,说明该约束被显式禁用(通过ALTER TABLE ... ALTER CHECK ... NOT ENFORCED) - 可直接测试:执行
INSERT INTO 表名 (列) VALUES (违反 CHECK 的值);—— 若没报错,说明约束未生效
定义 CHECK 时必须注意的语法细节
CHECK 表达式里不能引用子查询、存储函数、用户变量或临时表;仅支持确定性标量表达式。
- ✅ 合法:
CHECK (age >= 18 AND age 、<code>CHECK (status IN ('active', 'inactive')) - ❌ 非法:
CHECK ((SELECT COUNT(*) FROM logs) > 0)、CHECK (RAND() > 0.5)、CHECK (@var IS NOT NULL) - NULL 值默认绕过检查:比如
CHECK (price > 0)允许插入NULL,因为NULL > 0结果为UNKNOWN,不是FALSE - 如需禁止 NULL,必须额外加
NOT NULL约束
验证 CHECK 是否真正在运行
别只看建表语句是否成功,要实测行为:
- 用
INSERT插入明确违反条件的值(如age = -5),观察是否返回Check constraint 'xxx' is violated. - 检查
information_schema.CHECK_CONSTRAINTS表,确认CHECK_CLAUSE内容与预期一致 - 执行
SHOW CREATE TABLE 表名\G,确认输出中包含你定义的CHECK子句且无警告 - 注意:
DELETE操作**从不触发**任何CHECK约束校验,哪怕约束逻辑涉及行数或聚合(例如CHECK ((SELECT COUNT(*) FROM t) > 0))
当 CHECK 不可用或不够用时,替代方案怎么选
如果你用的是 8.0.15 或更低版本,或需要更复杂逻辑(如跨表校验、调用函数、动态规则),CHECK 就不能依赖。
- 首选触发器:
BEFORE INSERT/BEFORE UPDATE中用IF ... SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'xxx'; - 避免用存储过程封装校验逻辑再调用——它无法在 DML 自动触发,容易被绕过
- 应用层校验只是辅助,不能替代数据库级约束;网络延迟、并发写入、多服务共用 DB 等场景下必然漏检
- MySQL 9.6.0 起支持更严格的
ENFORCED控制粒度,但依然不支持非确定性表达式
实际约束是否起作用,永远取决于你手头那个具体版本 + 具体建表语句 + 实际执行时的插入行为。光看文档说“支持”,不亲手插一条违规数据试试,就等于没验证。











