Oracle ограничение, чтобы разрешить конкретное значение один раз на значение внешнего ключа - PullRequest
4 голосов
/ 21 ноября 2011

Допустим, у меня есть таблица parent с первичным ключом id и таблица child с внешним ключом parent_id и столбец типа «логический» (ограниченный 0 или 1), назовем его is_initial.

Я хочу наложить ограничение на child, чтобы для определенного значения parent_id была только одна строка с is_initial = 1.Может быть любое количество строк с is_initial = 0.

Может ли это быть сделано с ограничением?Я предпочитаю не добавлять триггер.

Спасибо.

Ответы [ 3 ]

4 голосов
/ 21 ноября 2011

Вы можете сделать это с уникальным индексом:

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

Теперь, когда вы попытаетесь вставить вторую строку с is_initial = 1, вы должны получить нарушение ограничения.

3 голосов
/ 21 ноября 2011

Вот, пожалуйста, я понимаю, что вы ищете сейчас

Обратите внимание на изменение уникального индекса:

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 голосов
/ 21 ноября 2011

Делайте это более «реляционным» способом - не используйте child.is_initial, вместо этого используйте parent.initial_child_id, который может быть НЕДЕЙСТВИТЕЛЕН, и КЛЮЧЕВОЙ КЛЮЧ к таблице child.

С 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;
...