【问题标题】:Conditional Unique Constraint SQL条件唯一约束 SQL
【发布时间】:2021-07-22 20:43:31
【问题描述】:

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

例如,我的数据库中有 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, 3, 2),因为前三个值已经存在一行。只有当 column_d 中的值已经存在并且其他值等于现有行时才可能。

更多帮助理解的例子:

Example insert result reason
(1, 2, 3, 1) accepted d is not unique and a, b, c got same values as the existing row with value 1 for column_d
(1, 2, 3, 4) rejected a, b ,c exists already in the table
(5,6,7,1) rejected 1 exists but with different values for a b and c
(3,4,5, 2) accepted d exists and a, b, c, have the same values
(7,8,9,3) accepted a, b, c are unique and d does not exist

【问题讨论】:

  • 您使用的是哪个数据库?例如,Oracle 和 SQL Server 将具有不同的语法和功能。我还认为您需要更多地扩展您的示例。显示需要拒绝插入的大量情况,以及应接受插入的大量情况。然后,可能的解决方案将涉及 CHECK 约束而不是 UNIQUE 约束:docs.microsoft.com/en-us/sql/relational-databases/tables/…(取决于您使用的数据库)
  • 不清楚为什么(1,2,3,2)不能加? column_d 的值 2 已存在于表中。
  • 我正在使用 Oracle。 @Serg (1, 2, 3, 2) 无法添加,因为 (1, 2, 3, 1) 已经在数据库中。如果 column_d 的值相等,则其他值也必须相等。我知道这没有意义,但在我的实际数据库中,我有更多列。我为我的问题简化了它,因为这些是相关的。但是很好,我忘了提到这一点,也许我自己也搞糊涂了。
  • 第二个@MatBailie 评论,更多示例可能会有所帮助。
  • 转述;如果...任何行已经存在具有相同 (a,b,c) 和不同 (d) 的情况,则插入必须被拒绝?

标签: sql oracle constraints unique-constraint


【解决方案1】:

据我了解,您有两个独特的约束(在 A,B,CD 上),如果您在 A,B,C,D 上有重复项,您希望抑制这两个约束。

这是 AFAIK 无法在一张表中完成的,因此您必须将设置拆分为两张表。

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

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

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

drop  table tab;
create table tab
(pk int,
 a int,
 b int,
 c int,
 d int, 
 primary key(pk),
 foreign key(a,b,c,d) references abcd(a,b,c,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,b,c,d) values (src.a, src.b, src.c, src.d)
    ;
END;
/

测试按预期运行

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

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

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

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

with abcd as (
select PK, A, B, C, D,
count(*) over (partition by A, B, C order by  PK) cnt_abc,
count(*) over (partition by D order by  PK) cnt_d,
count(*) over (partition by A, B, C, D order by PK) cnt_abcd
from tab)
select
   PK, A, B, C, D, 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

【讨论】:

    【解决方案2】:

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

    • 没有更多背景很难说

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

    a 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

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

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

    a 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_d       NUMBER(8) 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)
    )
    

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

    【讨论】:

      猜你喜欢
      • 2010-10-26
      • 1970-01-01
      • 2013-04-20
      • 2017-07-07
      • 1970-01-01
      • 1970-01-01
      • 2012-07-21
      • 2014-10-20
      相关资源
      最近更新 更多