MySQL的CHECK约束仅在8.0.16及以上版本真正生效;此前版本(含8.0.15、5.7等)虽支持语法但完全忽略校验,插入非法值不报错,需通过SELECT VERSION()确认版本并检查information_schema中ENFORCED='YES'。

MySQL 的 CHECK 约束在 8.0.16 及之后版本才真正生效;低于该版本(包括 8.0.15 及所有 5.7/5.6 版本)中定义的 CHECK 会被解析但完全忽略——插入非法数据不会报错。
确认你的 MySQL 版本是否真正支持 CHECK
执行 SELECT VERSION();,结果必须是 8.0.16 或更高。低于此版本时,哪怕语法正确、建表成功,CHECK 也形同虚设。例如:
CREATE TABLE t (x INT, CHECK (x > 0));
在 8.0.15 上能成功执行,但 INSERT INTO t VALUES (-1); 仍会成功——无任何错误。
- 检查约束是否实际启用:查询
information_schema.TABLE_CONSTRAINTS,确认CONSTRAINT_TYPE = 'CHECK'且ENFORCED = 'YES' - 低版本替代方案只能是触发器或应用层校验,不能依赖
CHECK语句本身 - 云数据库(如阿里云 RDS、腾讯云 CDB)默认可能未开启 enforce-check,需确认实例参数
check_constraint_enforced是否为ON
创建表时添加 CHECK 约束的两种写法
一种是列级约束(紧贴字段定义),一种是表级约束(放在所有字段之后)。两者语义一致,但命名和管理方式不同:
CREATE TABLE orders (
id INT PRIMARY KEY,
amount DECIMAL(10,2),
status VARCHAR(20),
-- 列级:隐式命名,MySQL 自动生成如 `orders_chk_1`
amount CHECK (amount >= 0),
-- 表级:显式命名,便于后续 ALTER
CONSTRAINT chk_status_valid CHECK (status IN ('pending', 'paid', 'shipped'))
);- 列级写法更紧凑,适合单字段简单规则(如
age CHECK (age BETWEEN 18 AND 65)) - 表级写法支持跨列逻辑(如
CHECK (end_date >= start_date)),且约束名可控 - 同一个字段不能重复加多个列级
CHECK,但可叠加多个表级CHECK
ALTER TABLE 添加或删除 CHECK 约束
已有表补加约束前,MySQL 会自动验证存量数据是否满足条件;若不满足,ALTER 直接失败并报错 ERROR 3819 (HY000): Check constraint ... is violated:
ALTER TABLE users ADD CONSTRAINT chk_phone_len CHECK (LENGTH(phone) = 11);
- 删除约束必须知道准确的约束名,可用
SHOW CREATE TABLE users;查看 - 删除语句是
ALTER TABLE users DROP CHECK chk_phone_len;(不是DROP CONSTRAINT) - 修改约束不可直接 ALTER,只能先
DROP再ADD
CHECK 约束的常见失效场景
CHECK 不校验 NULL 值,也不拦截 DELETE 操作——这是最容易被忽略的设计事实:
- 字段允许
NULL时,INSERT INTO t (x) VALUES (NULL);总是成功,即使写了CHECK (x > 0) -
DELETE FROM t WHERE x = -1;不触发任何CHECK验证,哪怕该约束本意是“禁止负值存在” - 表达式含子查询或函数(如
CHECK ((SELECT COUNT(*) FROM other_table) > 0))在 MySQL 中不被允许,会报错ERROR 3894 (HY000) - 正则匹配用
REGEXP可行,但性能敏感场景应避免(如高频插入的订单表里对email做复杂正则)
真正要靠约束兜底的业务逻辑,务必在 8.0.16+ 环境下验证 ENFORCED = 'YES',并预留触发器 fallback 路径——因为生产环境升级滞后、参数未开、或跨库迁移导致约束静默失效的情况太常见了。


















