条件唯一约束 SQL

问题描述

如果给定条件为真,有没有办法忽略唯一约束?

例如,我的数据库中有 3 列构成唯一约束:

create table example_table
  (
    column_primarykey RAW(16) default NULL not null,column_a number(8) not null,column_b number(8) not null,column_c number(8) not null,constraint constraint_1
          unique(column_a,column_b,column_c)
    constraint constraint_2
          primary key (column_primarykey)

现在我添加第四列:

alter table example_table
      add column_d number(8) not null,

我想要实现的是,如果 column_d 的值已存在于表中,则唯一约束将被忽略。如果 column_d 在表中不是唯一的,则唯一约束将被忽略,您可以将该行添加到表中。例如,这是我表中的现有数据(忽略不相关的主键原因):

column_a column_a column_c column_d
1 2 3 1
3 4 5 2

我想要的是您可以添加例如 (1,2,3,1) 但不能添加 (1,2) 因为已经有一行包含前三个值。仅当 column_d 中的值已存在且其他值等于现有行时才可能。

更多帮助理解的例子:

示例插入 结果 原因
(1,1) 接受 d 不是唯一的,a、b、c 的值与 column_d 的值为 1 的现有行相同
(1,4) 拒绝 a,b,c 已经存在于表中
(5,6,7,1) 拒绝 1 存在,但 a b 和 c 的值不同
(3,4,5,2) 接受 d 存在且 a、b、c 具有相同的值
(7,8,9,3) 接受 a、b、c 是唯一的,d 不存在

解决方法

听起来您好像试图将两张或多张桌子挤成一张桌子。

  • 没有更多背景很难判断

例如,如果你制作了一个大的平面文件,你可能会有这个?

一个 b c d x y z
1 2 3 1 1 3 1
1 2 3 1 2 8 7
1 2 3 1 5 9 2
4 5 6 2 9 8 7
4 5 6 2 4 5 6
4 5 6 2 3 2 1
4 5 6 2 2 1 0

不过,数据库不是电子表格或平面文件,它们是关系结构。

上面的文件在数据库中可能会更好地表示为两个表...

一个 b c d
1 2 3 1
4 5 6 2
d x y z
1 1 3 1
1 2 8 7
1 5 9 2
2 9 8 7
2 4 5 6
2 3 2 1
2 2 1 0

如果你想要一个新的“数据”行,你可以在第二个表中添加一行。

如果要在 (a,b,c)(d) 之间创建新关系,请向第一个表中添加一行。


可以按如下方式实施和执行...

CREATE TABLE map (
    column_a       NUMBER(8) NOT NULL,column_b       NUMBER(8) NOT NULL,column_c       NUMBER(8) NOT NULL,column_d       NUMBER(8) NOT NULL,UNIQUE(column_a,column_b,column_c),UNIQUE(column_d)
)

CREATE TABLE fact (
    column_pk      RAW(16)   NOT NULL,column_x       NUMBER(8) NOT NULL,column_y       NUMBER(8) NOT NULL,column_z       NUMBER(8) NOT NULL,PRIMARY KEY (column_pk),FOREIGN KEY (column_d) REFERENCES map(column_d)
)

据我所知,这个结构可以包含您想要允许的所有内容,也可以禁止您想要禁止的所有内容。

,

据我所知,您有两个独特的约束(在 A,B,CD 上),如果您在 A,C,D 上有重复,您想抑制这两个约束。

AFAIK 无法在一张桌子上完成此操作,因此您必须将设置拆分为两张桌子。

第一个 ABCD 检查约束,但不允许重复,第二个 TAB 存储实际数据并引用第一个表。

插入前触发器中,A,D 上的唯一值合并在第一个表中。

create table abcd
(a int,b int,c int,d int,constraint abcd unique (a,c,d),constraint abc unique (a,c),constraint d unique(d));

drop  table tab;
create table tab
(pk int,a int,primary key(pk),foreign key(a,d) references abcd(a,d)
 );
 
CREATE OR REPLACE TRIGGER tab_trigger
  BEFORE  INSERT ON tab
  FOR EACH ROW
BEGIN
   merge into abcd using
   (select  :new.a a,:new.b b,:new.c c,:new.d d from dual) src
    on  (abcd.a = src.a and abcd.b = src.b and abcd.c = src.c and abcd.d = src.d)
    when not matched then insert (a,d) values (src.a,src.b,src.c,src.d)
    ;
END;
/

测试按预期运行

insert into tab(PK,A,D) values (1,1,2,3,1);
1 row inserted.
insert into tab(PK,D) values (2,4,5,2);
1 row inserted.
insert into tab(PK,D) values (3,D) values (4,4);
ORA-00001: unique constraint (ZZZ.ABC) violated
insert into tab(PK,D) values (5,6,7,1);
ORA-00001: unique constraint (ZZZ.D) violated
insert into tab(PK,D) values (6,D) values (7,8,9,3);
1 row inserted.

无论如何我必须承认我不喜欢基于触发器的解决方案,我更喜欢带有检查视图的延迟验证

即您可以插入任何行,但在视图中您会看到哪些行是无效的,并且您可以处理它。

验证查询相当简单,它使用分析函数来获取键的重复计数

with abcd as (
select PK,D,count(*) over (partition by A,C order by  PK) cnt_abc,count(*) over (partition by D order by  PK) cnt_d,D order by PK) cnt_abcd
from tab)
select
   PK,CNT_ABC,CNT_D,CNT_ABCD,case when (CNT_ABC > 1 or CNT_D > 1) and CNT_ABCD = 1 then 'rejected' 
   else 'accepted' end as status
from abcd   
order by PK;

        ID          A          B          C          D    CNT_ABC      CNT_D   CNT_ABCD STATUS  
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- --------
         1          1          2          3          1          1          1          1 accepted
         2          3          4          5          2          1          1          1 accepted
         3          1          2          3          1          2          2          2 accepted
         4          1          2          3          4          3          1          1 rejected
         5          5          6          7          1          1          3          1 rejected
         6          3          4          5          2          2          2          2 accepted
         7          7          8          9          3          1          1          1 accepted