如何实现两列至少一列非空的复合约束(Liquibase/PostgreSQL场景)
两列至少一列非空的约束实现方案
这类约束本质是多列组合的业务校验规则,核心逻辑是对两个目标列做组合判断,只要任意一列存在有效业务值就允许数据入库,不属于单列NOT NULL的基础约束范畴,具体实现路径分三类说明:
Liquibase 工具的原生支持能力
Liquibase 从 3.5.0 版本开始就原生支持配置CHECK约束,不需要依赖自定义SQL就能直接落地这类跨列校验规则:
- 配置时不需要逐列设置NOT NULL,只需要在changeSet中通过
addCheckConstraint标签写入组合判断逻辑即可,示例配置参考:<changeSet id="chk_contact_at_least_one" author="data_ops"> <addCheckConstraint tableName="sys_user" constraintName="chk_user_contact_valid" constrainedColumns="mobile,email" conditionConstraint="mobile IS NOT NULL OR email IS NOT NULL" /> </changeSet> - 实际使用注意事项:低版本Liquibase(3.5.0之前)没有独立的CHECK约束配置入口,这类场景需要通过
sql标签直接执行原生DDL语句实现约束创建;Liquibase透传CHECK逻辑时不会修改底层校验语法,只要目标数据库支持CHECK约束,就能保证约束逻辑正常生效。
通用SQL的实现方式
对于所有兼容SQL标准CHECK约束的数据库(包括PostgreSQL、MySQL 8.0.16及以上版本、SQL Server、Oracle等),都可以通过统一的ALTER语句创建这类约束,不存在数据库语法差异:
-- 通用约束创建语法 ALTER TABLE 目标业务表 ADD CONSTRAINT chk_两列至少一非空 CHECK ( 第一列 IS NOT NULL OR 第二列 IS NOT NULL );
这类约束属于数据库内置的行级校验规则,在数据插入、更新时自动触发校验,不存在触发器类方案的性能开销,也不会因为运维操作误关校验逻辑导致脏数据入库。
PostgreSQL 专属的简化实现方式
PostgreSQL 在CHECK约束的基础上提供了更灵活的内置函数和语法,能简化多列非空判断的配置:
- 对于列数较多的非空校验场景,可以直接用内置函数
num_nonnulls自动统计多列中的非空值个数,不需要手写长串OR判断逻辑,比如要求mobile/email/wechat三列至少1个有效非空值,约束逻辑可以简化为:ALTER TABLE sys_user ADD CONSTRAINT chk_multi_contact_valid CHECK (num_nonnulls(mobile, email, wechat) >= 1); - 如果需要把“空字符串、纯空白字符串”也判定为无有效业务值,可以结合PostgreSQL的正则匹配语法调整校验逻辑,把非空判断和业务有效值判断结合,示例:
相比通用SQL只能判断NULL的局限,这种写法能更贴合实际业务的校验要求。ALTER TABLE sys_user ADD CONSTRAINT chk_contact_real_value CHECK ( (mobile IS NOT NULL AND mobile !~ '^\s*$') OR (email IS NOT NULL AND email !~ '^\s*$') );
内容的提问来源于stack exchange,提问作者LeYAUable
相关产品推荐
相关产品推荐

