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 约束只约束后续写入:加约束成功后旧数据不会自动被重新校验,存量修正与约束添加要按「先清理、后约束」的顺序做。