MySQL 8.0+才真正支持CHECK约束,低版本会静默忽略;需用SELECT VERSION()确认版本,添加时须显式命名且防重复;INT UNSIGNED是5.7+兼容的非负替代方案;务必测试INSERT和UPDATE双场景。

MySQL 8.0+ 才真正支持 CHECK 约束
低于 8.0 的 MySQL 版本(如 5.7)会**静默忽略** CHECK 子句,既不报错也不生效。你执行 ALTER TABLE ... ADD CONSTRAINT ... CHECK(...) 看似成功,但插入负数仍能通过。务必先确认版本:SELECT VERSION();。若输出是 8.0.x 或更高,才能依赖 CHECK;否则得用应用层校验或触发器兜底。
ADD CONSTRAINT 必须指定约束名,且不能重复
给已有表加非负约束时,ALTER TABLE 语句必须显式命名约束,例如:ALTER TABLE accounts ADD CONSTRAINT chk_balance_non_negative CHECK (balance >= 0);。如果重复执行该语句,会报错 ERROR 1022 (HY000): Can't write; duplicate key in table。解决方法是先查是否存在:SELECT constraint_name FROM information_schema.check_constraints WHERE constraint_schema = 'your_db' AND table_name = 'accounts';,再决定是否 DROP CONSTRAINT。
数值类型本身不阻止负数,UNSIGNED 是更底层的替代方案
如果你只需要“非负整数”,比 CHECK 更轻量、兼容性更好的方式是直接用 INT UNSIGNED 或 TINYINT UNSIGNED。它从存储层就禁止负值,且 5.7+ 全支持。例如:ALTER TABLE products MODIFY stock INT UNSIGNED;。注意两点:一是修改后原有负数会被截断为 0(可能丢失数据),二是 UNSIGNED 仅适用于整数类型,DECIMAL 不支持该属性,此时只能靠 CHECK。
测试约束是否生效,别只靠 INSERT
很多人只验证插入,却漏掉更新场景。一个真正生效的约束必须同时拦截 INSERT 和 UPDATE。务必测试这两条:INSERT INTO accounts (balance) VALUES (-50); 和 UPDATE accounts SET balance = -10 WHERE user_id = 1;。如果其中一条没报错,说明约束未生效——大概率是 MySQL 版本太低,或约束名已存在导致命令实际未执行。
约束真正起作用的地方,往往在你忘记检查 UPDATE 的那一刻。


















