Проблемы со ссылками на циклические внешние ключи Oracle - PullRequest
0 голосов
/ 16 ноября 2009

Я ломал голову, пытаясь найти решение этой проблемы.

Для класса базы данных мне нужно реализовать следующее:

Table HUSBANDS: (Name Varchar2(10)) (Wife Varchar2(10))
Table WIVES: (Name Varchar2(10)) (Husband Varchar2(10))

и с использованием ограничений Oracle, в соответствии со следующими правилами:

  1. Ни у одного мужа не может быть одинакового имени
  2. Никакие две жены не могут иметь одинаковое имя
  3. У каждой жены должен быть один и только один муж
  4. У каждого мужа должна быть одна и только одна жена

Пока что я реализовал таблицу в Oracle SQL:

create table husbands(
  name varchar2(10) not null
  , wife varchar2(10) not null
);
create table wives(
  name varchar2(10) not null
  , husband varchar2(10) not null
);

Я почти уверен, что решил пункты 1 и 2, используя правильные первичные ключи:

alter table husbands
  add constraint husbands_pk
  primary key(name);
alter table wives
  add constraint wives_pk
  primary key(name);

И вот тут я сталкиваюсь с проблемами. Я решил использовать внешние ключи для реализации шагов 3 и 4:

alter table husbands
  add constraint husbands_fk_wife
  foreign key(wife)
  references wives(name);
alter table wives
  add constraint wives_fk_husband
  foreign key(husband)
  references husbands(name);

Теперь тестовый пример, который использует мой профессор, - это возможность добавить супружескую пару в базу данных. Проблема, которую я имею, состоит в том, как сделать это, используя только ограничения. Если бы я хотел добавить Джека и Джилл в качестве супружеской пары, нельзя добавить мужа, пока жена не будет добавлена. жена не может быть добавлена ​​до тех пор, пока не будет добавлен муж.
Я думаю, что моя проблема заключается в использовании внешних ключей. В этой ситуации может сработать проверочное ограничение, но я не могу понять, как оно будет работать.

Ответы [ 8 ]

3 голосов
/ 16 ноября 2009

Необходимость использования отложенных ограничений часто указывает на проблемы проектирования. Конечно, эта модель данных не очень хорошая: она не нормализована должным образом. Нормализованное решение будет выглядеть так:

PERSON
------
ID number 
NAME varchar2(30)
PRIMARY KEY (ID)


MARRIED_COUPLE
--------------
PARTNER_1 number
PARTNER_2 number
PRIMARY KEY (PARTNER_1, PARTNER_2)
FOREIGN KEY (PARTNER_1) REFERENCES (PERSON.ID)
FOREIGN KEY (PARTNER_2) REFERENCES (PERSON.ID)

Это дает дополнительное преимущество поддержки гражданских партнерств :) Если вы хотите препятствовать двоеженству, вы можете добавить уникальные ключи на PARTNER_1 или PARTNER_2.

Сложнее моделировать культуры, где допускается многоженство или полиандрия.

редактировать

На что Дэвид возражает (в комментариях):

SQL> create table married_couple (partner_1 number, partner_2 number)
  2  /

Table created.

SQL> alter table married_couple add primary key (partner_1, partner_2)
  2  /

Table altered.

SQL> insert into married_couple values (1, 2)
  2  /

1 row created.

SQL> insert into married_couple values (2,1)
  2  /

1 row created.

SQL> 

Это верная точка, но она разрешима. Например, с Oracle я могу создать уникальную функцию на основе, чтобы обеспечить уникальность перестановок.

SQL> delete from married_couple
  2  /

2 rows deleted.

SQL> create unique index mc_uidx on married_couple 
  2     (greatest(partner_1, partner_2),least(partner_1, partner_2))
  3  /

Index created.

SQL> insert into married_couple values (1, 2)
  2  /

1 row created.

SQL> insert into married_couple values (2,1)
  2  /
insert into married_couple values (2,1)
*
ERROR at line 1:
ORA-00001: unique constraint (APC.MC_UIDX) violated


SQL>

Чтобы избежать многоженства, мы можем использовать аналогичный прием. Мы не хотим этого:

SQL> insert into married_couple values (1,3)
  2  /

1 row created.

Итак, нам нужны два индекса:

SQL> delete from married_couple where partner_2 = 3;

1 row deleted.

SQL> create unique index mc1_uidx
  2      on married_couple (greatest(partner_1, partner_2))
  3  /

Index created.

SQL> create unique index mc2_uidx
  2      on married_couple (least(partner_1, partner_2))
  3  /

Index created.

SQL> insert into married_couple values (3, 1)
  2  /
insert into married_couple values (3, 1)
*
ERROR at line 1:
ORA-00001: unique constraint (APC.MC2_UIDX) violated


SQL>

Для тех, кто думает, что это обман для решения проблемы моделирования данных с помощью хитрости реализации, я умоляю «Виновен как обвиненный», но у меня был долгий и трудный день.

3 голосов
/ 16 ноября 2009

Альтернативой отсроченным ограничениям является третья таблица (муж, жена) с двумя уникальными ограничениями (одно для мужа, одно для жены) и имеющая ограничения ссылочной целостности между этим и таблицей мужей и таблицей жен. Столбцы жены / мужа в таблицах мужей / жен будут избыточными и должны быть удалены.

PS. Должно ли это быть ЖЕНЫ, а не ЖЕНЫ?

2 голосов
/ 16 ноября 2009

Изучите отложенные ограничения (не новый тип, просто параметр для существующих), пока вы добились успеха.

1 голос
/ 16 ноября 2009

Глупая идея - почему бы просто не иметь одну таблицу «Пары» со столбцами «Husband_Name» и «Wife_Name», каждая из которых имеет уникальное ограничение? Мне кажется, что это удовлетворяет всем требованиям. :)

1 голос
/ 16 ноября 2009

Отсроченные ограничения - верный способ сделать это. Интересно, что есть альтернативный способ - с вашей настройкой и Oracle 10gR2:

SQL> CREATE OR REPLACE TRIGGER husband_wife_trg AFTER INSERT ON husbands
  2  FOR EACH ROW
  3  BEGIN
  4     INSERT INTO wives VALUES (:new.wife, :new.name);
  5  END;
  6  /

Trigger created

SQL> INSERT INTO husbands VALUES ('Husband A', 'Wife B');

1 row inserted

SQL> SELECT * FROM wives;

NAME       HUSBAND
---------- ----------
Wife B     Husband A

Мне не нравится помещать транзакционную логику в триггеры, но если вы идете по этому пути, вам не нужны отложенные ограничения.

0 голосов
/ 02 апреля 2014

Извините - в большинстве ответов не указана конкретная проблема:

"ДОЛЖЕН ИМЕТЬ ОДИН И ТОЛЬКО ОДИН"

Это, по сути, означает: ВЫ НЕ МОЖЕТЕ ВСТАВИТЬ ОДНОГО ЧЕЛОВЕКА в БД !!! * Потому что у одного человека не будет ровно одного партнера!

Таким образом, единственными допустимыми решениями являются:

  1. Отсроченные ограничения: как можно проще - просто пометьте свои ограничения как отсроченные, а затем вставьте жену и мужа, и он будет проверять целостность только после принятия (я не знаю, на что люди жалуются - это не обман или странность ... Это обычная практика и единственный способ во многих коммерческих случаях !!!)

  2. INSERT ALL - по крайней мере, в более новых версиях Oracle вы можете использовать оператор INSERT ALL, который будет вставляться в несколько таблиц одновременно. Таким образом, вы можете написать одну «Вставить жену и hsuband», которая подходит для многих вариантов использования.

  3. Триггер: в этом особом случае Триггер сделает свое дело - но как только у вас появятся дополнительные атрибуты, он больше не будет работать ...

Но все остальные ответы были просто неверны для предложенной задачи: два объекта с обязательным 1 к 1 соединением

0 голосов
/ 16 ноября 2009

Вам нужна третья таблица, не только для ее исправления, но и для правильной обработки многоженства / бигамия, что разрешено более чем в 40 странах мира.

0 голосов
/ 16 ноября 2009

1) setAutoCommit () как false 2) Вставляет записи в обе таблицы в одну единицу работы. 3) фиксация

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...