”是数据库中用于限制表中某列或某几列取值范围的一种完整性约束机制)
“检查约束CHECK”是数据库中用于限制表中某列或某几列取值范围的一种完整性约束机制它基于布尔表达式返回 TRUE/FALSE在插入或更新数据时自动验证若不满足则拒绝操作。而“自定义业务规则”通常指超出标准 SQL 约束能力的、更复杂、更贴近实际业务逻辑的校验要求例如订单金额必须大于等于所有明细行金额之和涉及多表/聚合员工入职日期不得晚于其部门成立日期跨表引用某状态变更需满足前置审批流程已完成依赖外部系统或状态机日期范围不能与其他已存在记录重叠需子查询或函数支持。⚠️ 注意标准 CHECK 约束在多数数据库如 PostgreSQL、SQL Server中支持简单表达式如age 18,status IN (A,I)但不支持子查询、窗口函数、跨表引用或调用存储过程。因此复杂业务规则往往需通过以下方式实现✅ 触发器TRIGGER——在 INSERT/UPDATE/DELETE 时执行自定义逻辑✅ 应用层校验——在服务代码中统一控制推荐作为主防线 数据库约束为兜底✅ 存储过程封装写操作——确保所有写入都经由受控接口✅ 使用函数索引 CHECKPostgreSQL 支持带函数的 CHECK如CHECK (is_valid_date_range(start_date, end_date))前提是函数为IMMUTABLE。简言之 CHECK 约束 轻量、高效、声明式、单行/单表内校验 自定义业务规则 灵活、复杂、可能跨表/跨事务/含副作用需结合触发器、应用逻辑或领域服务实现。-- 示例合法的 CHECK 约束单表、确定性表达式ALTERTABLEordersADDCONSTRAINTchk_order_amount_positiveCHECK(total_amount0ANDdiscount_rateBETWEEN0AND1);CHECK 约束不能引用其他表的字段——这是 SQL 标准ISO/IEC 9075明确限定的也是主流关系型数据库如 PostgreSQL、SQL Server、Oracle、MySQL 8.0 的严格模式普遍禁止的行为。✅ 原因解析核心是“完整性约束的语义与实现限制”违反 CHECK 的设计定位CHECK 是行级/表级的静态谓词约束仅作用于当前正在插入或更新的行或该表内其他行如EXCLUDE在 PostgreSQL 中属特例其表达式必须是确定性deterministic、无副作用、不依赖外部状态的。跨表引用会引入外部表数据的动态性如被并发修改潜在的锁竞争与死锁风险查询计划不可预测影响 DML 性能与原子性。破坏事务隔离与一致性保障若允许CHECK (user_id IN (SELECT id FROM users))则该子查询在约束校验时执行可能读到未提交数据取决于隔离级别导致误判若users表被其他事务删除/修改CHECK 校验结果将不稳定更严重的是外键FOREIGN KEY才是专门为此类引用完整性设计的机制它已内置级联、延迟检查、索引优化等能力。标准兼容性与实现复杂性SQL-92 及后续标准明确规定 CHECK 表达式中不得包含子查询除非是标量子查询且受限极严但跨表仍被排除。实现跨表 CHECK 需深度耦合查询优化器、事务管理器和锁管理器大幅增加数据库内核复杂度得不偿失。替代方案更优、更清晰✅外键约束FOREIGN KEY专用于保证引用完整性如order.user_id → users.id支持索引加速、ON DELETE/UPDATE 行为、可延迟DEFERRABLE等✅触发器TRIGGER当需复杂逻辑如“订单客户等级 ≥ 3 才能下单”时用BEFORE INSERT/UPDATE触发器显式查询并校验✅应用层 数据库双校验外键兜底 应用服务预检兼顾性能与业务语义。 补充说明PostgreSQL 的EXCLUDE约束基于 GiST/GIN 索引虽可实现“时间范围不重叠”等跨行逻辑但仍局限于本表不涉及其他表字段。-- ❌ 错误示例所有主流数据库均报错ALTERTABLEordersADDCONSTRAINTchk_user_existsCHECK(user_idIN(SELECTidFROMusers));-- 语法错误不允许子查询-- ✅ 正确替代使用外键ALTERTABLEordersADDCONSTRAINTfk_orders_user_idFOREIGNKEY(user_id)REFERENCESusers(id)ONDELETECASCADE;