Oracle 对特定值的唯一约束

Oracle Unique constraint on certain value

我创建了一个 table (test_table),它有两列 (test_IP、test_STATUS)。 table 可以有任意数量的 IP,但是,只能允许一个状态为“活动”的 IP。可以允许任何数量的“非活动”状态。供参考(活跃 = 1,不活跃 = 2)

例如:

test_IP           test_STATUS
==============================
1.2.3.4            1   
1.2.3.5            2   
1.2.3.6            2   
1.2.3.7            2    
1.2.3.8            1 -- this should now fail (as there are multiple values containing 1 (Active))    

是否可以对特定值进行唯一约束?如果可以,有人可以帮助我如何实现这一目标吗?提前致谢!

如果 test_status 列可以包含 null 值并且 1 值表示该 ip 处于活动状态,而不是 2 您使用 null 表示不活动,那么唯一约束将起作用。

但我认为这不是一个非常优雅的解决方案。

使用基于唯一函数的索引,它只考虑活动行并将非活动键重置为NULL

例子

select * from tab order by 1,2;

TEST_IP TEST_STATUS
------- -----------
1.1.1.1           1
1.1.1.1           2
1.1.1.1           2
1.1.1.1           2
1.1.1.2           1

create unique index tab_ixd on tab (case when  test_STATUS = 1 then test_IP end);

insert into tab (test_IP,test_STATUS) values ('1.1.1.1', 2);
1 row inserted.

insert into tab (test_IP,test_STATUS) values ('1.1.1.1', 1);
ORA-00001: unique constraint (ZZZZ.TAB_IXD) violated

如果允许每个 IP 地址一个活动行,则上述解决方案有效。如果您只需要对整个 table 的一个活动行 进行约束,请使用:

create unique index tab_ixd on tab (case when  test_STATUS = 1 then test_STATUS  end);

我会添加虚拟不可见列is_active:

alter table ip_list
   add is_active varchar2(1) 
       invisible 
       generated always as 
          (case when test_STATUS=1 then 'y' end) virtual;

alter table ip_list add constraint uq_active_ip unique (is_active);

由于它是不可见的,因此不会影响现有查询。只有在查询中指定 is_active 才能获取它。

完整示例:

SQL> create table ip_list(test_IP,test_STATUS)
  2  as
  3  select '1.2.3.4', 1 from dual union all
  4  select '1.2.3.5', 2 from dual union all
  5  select '1.2.3.6', 2 from dual union all
  6  select '1.2.3.7', 2 from dual ;

Table created.

SQL> alter table ip_list add is_active varchar2(1) invisible generated always as (case when test_STATUS=1 then 'y' end) virtual;

Table altered.

SQL> alter table ip_list add constraint uq_active_ip unique (is_active);

Table altered.

SQL> insert into ip_list(test_ip, test_status) values('1.2.3.8',1);
insert into ip_list(test_ip, test_status) values('1.2.3.8',1)
*
ERROR at line 1:
ORA-00001: unique constraint (XTENDER.UQ_ACTIVE_IP) violated

-- note that * does not return this new column:
SQL> select * from ip_list;

TEST_IP TEST_STATUS
------- -----------
1.2.3.4           1
1.2.3.5           2
1.2.3.6           2
1.2.3.7           2

-- but you can use it to filter active rows:
SQL> select * from ip_list where is_active='y';

TEST_IP TEST_STATUS
------- -----------
1.2.3.4           1

1 row selected.