Oracle约束允许每个外键值一次特定值

时间:2011-11-21 17:57:12

标签: sql oracle constraints ddl

假设我有一个带有主键parent的表id,带有外键child的表parent_id和一个“布尔”列(约束为0)或1),我们称之为is_initial

我想要做的是对child施加约束,以便对于parent_id的特定值,只有一行is_initial = 1。 is_initial = 0可以有任意数量的行。

这可以通过约束来完成吗?我不想添加触发器。

感谢。

3 个答案:

答案 0 :(得分:4)

您可以使用唯一索引执行此操作:

create unique index initialindex on child(
  case when is_initial <> 1 then parent_id || 'xx' || child_id 
       else null
  end
);

现在,在尝试使用is_initial = 1插入第二行后,您应该会遇到约束违规。

答案 1 :(得分:3)

在这里,我相信我明白你在寻找什么

请注意唯一索引中的更改:

create unique index childTable_initialIndex on childTable(
  case when is_initial = 1 then parent_id 
  else null
  end);

修改后的代码

create table childTable(parent_id number, child_id number primary key, is_initial number, somethingelse varchar2(50) );

create unique index childTable_initialIndex on childTable(
  case when is_initial = 1 then parent_id 
  else null
  end);


  insert into childTable(parent_id, child_id, is_initial,somethingelse) values (1,1,0,'works');
1 rows inserted.  

  insert into childTable(parent_id, child_id, is_initial,somethingelse) values (1,1,0,'will not work if childId is pk');
SQL Error: ORA-00001: unique constraint (SYS_C0062138) violated
00001. 00000 -  "unique constraint (%s.%s) violated"
*Cause:    An UPDATE or INSERT statement attempted to insert a duplicate key.
           For Trusted Oracle configured in DBMS MAC mode, you may see
           this message if a duplicate entry exists at a different level.
*Action:   Either remove the unique restriction or do not insert the key  

  insert into childTable(parent_id, child_id, is_initial,somethingelse) values (1,2,1,'works3');
1 rows inserted.  

  insert into childTable(parent_id, child_id, is_initial,somethingelse) values (1,3,1,'should not work');
SQL Error: ORA-00001: unique constraint (CHILDTABLE_INITIALINDEX) violated
00001. 00000 -  "unique constraint (%s.%s) violated"
*Cause:    An UPDATE or INSERT statement attempted to insert a duplicate key.
           For Trusted Oracle configured in DBMS MAC mode, you may see
           this message if a duplicate entry exists at a different level.
*Action:   Either remove the unique restriction or do not insert the key.  

  insert into childTable(parent_id, child_id, is_initial,somethingelse) values (2,4,0,'works4');
1 rows inserted.

  insert into childTable(parent_id, child_id, is_initial,somethingelse) values (2,5,0,'works5');
1 rows inserted.

  insert into childTable(parent_id, child_id, is_initial,somethingelse) values (2,6,1,'works6');
1 rows inserted.

  insert into childTable(parent_id, child_id, is_initial,somethingelse) values (2,7,1,'should not work');
SQL Error: ORA-00001: unique constraint (CHILDTABLE_INITIALINDEX) violated
00001. 00000 -  "unique constraint (%s.%s) violated"
*Cause:    An UPDATE or INSERT statement attempted to insert a duplicate key.
           For Trusted Oracle configured in DBMS MAC mode, you may see
           this message if a duplicate entry exists at a different level.
*Action:   Either remove the unique restriction or do not insert the key.  
  --we should only see things that work
  select * from childTable
/ 

--this should not work, since works already has the 1/1 is_initial 1
update childTable 
   set somethingelse = 'Should not work!'
     , is_initial    = 1
 where somethingelse = 'works';

SQL Error: ORA-00001: unique constraint (CHILDTABLE_INITIALINDEX) violated
00001. 00000 -  "unique constraint (%s.%s) violated"
*Cause:    An UPDATE or INSERT statement attempted to insert a duplicate key.
           For Trusted Oracle configured in DBMS MAC mode, you may see
           this message if a duplicate entry exists at a different level.
*Action:   Either remove the unique restriction or do not insert the key.

结果如下:

PARENT_ID CHILD_ID IS_INITIAL SOMETHINGELSE                                    
--------- -------- ---------- --------------------------------------------------
        1        1          0 works                                              
        1        2          1 works3                                             
        2        4          0 works4                                             
        2        5          0 works5                                             
        2        6          1 works6  

答案 2 :(得分:2)

以更“关系”的方式执行 - 不要使用child.is_initial,而是使用可以为NULL的parent.initial_child_id,并且是child表的FOREIGN KEY。

由于initial_child_id位于parent表格而非child,因此每个家长只能有一个。

你的DDL看起来像这样:

CREATE TABLE parent (
    id INT,
    initial_child_id INT,
    PRIMARY KEY (id)
);

CREATE TABLE child (
    child_id INT,
    parent_id INT NOT NULL,
    PRIMARY KEY (child_id)
);

ALTER TABLE parent ADD FOREIGN KEY (initial_child_id) REFERENCES child;
ALTER TABLE child ADD FOREIGN KEY (parent_id) REFERENCES parent;