SQL与Oracle数据库表约束:禁止重复插入同名称且薪资为空的记录
核心管控规则说明
- 约束生效边界:当表中已存在某Name字段值、且对应salary为NULL的记录时,禁止再插入相同Name值、salary为NULL的重复行
- 约束豁免范围:同Name值下,salary为非NULL值的记录,无论插入多少条都不受规则限制,可正常写入
- 典型场景验证:表中已存在
Id=1、Name=A、age=30、salary=NULL的记录时,插入Id=3、Name=A、salary=NULL的重复行必须被拦截
数据库层实现方案
通用SQL实现(PostgreSQL、SQL Server、MySQL 8.0+)
对于原生支持部分过滤索引的数据库,不需要复杂逻辑,直接创建带范围限定的唯一索引即可实现规则,性能最优。
首先创建示例业务表:
CREATE TABLE employee ( Id INT PRIMARY KEY, Name VARCHAR(50) NOT NULL, age INT, salary DECIMAL(10,2) );
然后创建带过滤条件的唯一约束,仅对salary为NULL的行做Name值的唯一性校验:
CREATE UNIQUE INDEX uk_employee_name_null_salary ON employee (Name) WHERE salary IS NULL;
*效果说明:
- 插入Name=A、salary=NULL的第一条记录时,可正常写入
- 再次插入同Name、同salary为NULL的重复行时,触发唯一索引冲突,直接被拦截
- 所有salary为非NULL的记录,均不在索引校验范围内,不会被规则误拦截*
Oracle 实现方案
Oracle 不支持普通唯一索引附带WHERE过滤条件,因此利用Oracle唯一索引的原生特性实现完全相同的管控效果:
Oracle 唯一索引不会对「索引键值为全NULL」的行做唯一性校验,多个NULL键值可同时存在于索引中,不会触发冲突。
基于该特性,通过CASE表达式生成索引键值,将需要约束的行映射为可校验的非空键值,不需要约束的行映射为NULL键值:
CREATE UNIQUE INDEX uk_employee_name_null_salary ON employee ( CASE WHEN salary IS NULL THEN Name ELSE NULL END );
*效果说明:
- 当插入salary为NULL、Name=X的记录时,索引键值为Name值X,第一次插入可正常通过校验;后续重复插入同规则的行时,因键值X重复触发唯一冲突,被拦截
- 当插入任意salary非NULL的记录时,索引键值为NULL,不会被纳入唯一性校验范围,可自由插入,无规则误伤*
方案优势
- 均为数据库引擎层面的原生约束,并发写入场景下可保证规则的原子性生效,不存在触发器/应用层校验的并发幻读漏洞
- 校验逻辑由数据库优化器自动执行,性能远高于行级触发器方案
内容的提问来源于stack exchange,提问作者user1494438
相关产品推荐
相关产品推荐

