微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Oracle 对特定值的唯一约束

如何解决Oracle 对特定值的唯一约束

我创建了一个表(test_table),它有两列(test_IP、test_STATUS)。该表可以有任意数量的 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))    

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

解决方法

使用基于唯一函数的索引,只考虑活动行并将非活动键重置为 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,1);
ORA-00001: unique constraint (ZZZZ.TAB_IXD) violated

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

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,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.
,

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。