首页/数据/SQL 速查/CHECK 约束
变更与事务52 条

CHECK 约束

用表达式限制列值范围:MySQL 8.0.16 起真正强制执行,PostgreSQL 长期支持;违反时写入直接报错。

SQL 速查数据更新于 2026-10-13

CHECK 约束做什么

CHECK 约束用表达式限制列值:金额必须为正、状态必须在允许列表里,违反时写入直接报错。MySQL 从 8.0.16 起才真正强制执行,更早版本只解析不检查;PostgreSQL 长期支持。它把最基础的校验挡在数据库层,但不能替代业务规则的完整校验。

基本信息

强制执行MySQL 8.0.16 起生效;更早版本解析但静默忽略
PostgreSQL长期支持,行为一致
NULL 语义CHECK (col > 0) 对 NULL 结果为未知,不拦截 NULL
命名显式命名便于定位与后续删除
删除方式ALTER TABLE ... DROP CONSTRAINT 约束名

典型使用场景

  • 金额、数量必须为正数
  • 状态字段限制在枚举范围内
  • 日期范围校验,例如结束日期不早于开始日期
  • 拦住明显越界的数据,作为应用校验之外的兜底
  • 新建表时一次性把基础规则写进 DDL,防止后续写入绕过

示例

示例
-- 金额必须为正
ALTER TABLE orders
  ADD CONSTRAINT chk_total_positive CHECK (total > 0);

-- 多条件组合:状态必须在允许列表里
ALTER TABLE orders
  ADD CONSTRAINT chk_status_valid
  CHECK (status IN ('new', 'paid', 'shipped', 'done'));

-- 删除约束
ALTER TABLE orders DROP CONSTRAINT chk_total_positive;

各层校验能拦住什么

机制能拦住局限
CHECK 约束单行内的值域与列间关系不跨表,NULL 不拦截
NOT NULL列必须有值不限制取值范围
外键引用完整性不限制取值域
唯一约束重复值与取值范围无关
应用层校验复杂业务规则绕过接口直接写库时失效
在老版本 MySQL(8.0.16 之前)上依赖 CHECK:约束会被解析但不执行,脏数据照样写入。这类环境里校验必须同时放在应用层,或者升级后再补约束。
加约束前先清理存量数据:已有行不满足表达式时 ALTER TABLE 会直接失败,需要先把历史数据修正,再统一加上约束。
约束命名建议带表名前缀:报错信息里出现 chk_orders_total_positive 这类名字时,一眼就能定位到表和规则,跨库迁移时也不容易撞名。
CHECK 约束只约束后续写入:加约束成功后旧数据不会自动被重新校验,存量修正与约束添加要按「先清理、后约束」的顺序做。

易错点

  • 在老版本 MySQL 上依赖 CHECK:8.0.16 之前不强制执行,脏数据照样写进去,校验要落到应用层
  • 忽略 NULL 语义:CHECK (total > 0) 不会拒绝 NULL,需要配合 NOT NULL 才严密
  • 存量数据不满足表达式就加约束:ALTER TABLE 直接失败,先修正历史数据再添加
  • 用 CHECK 做跨表校验:它只能看本行数据,跨表一致性要靠外键或应用层
  • 约束不命名:出错时提示里只有随机名字,排查与删除都费劲,建约束时就显式命名

数据来源与参考

  • MySQL 8.0 Reference Manual · CHECK Constraints
  • PostgreSQL Documentation · Constraints
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌