Oracle循环外键引用问题

时间:2009-11-16 03:29:21

标签: sql foreign-keys

我一直绞尽脑汁想要找到解决方案。

对于数据库类,我需要实现以下内容:

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

并使用Oracle约束,以下规则:

  1. 没有两个丈夫可以拥有相同的名字
  2. 没有两个妻子可以拥有相同的名字
  3. 每个妻子必须有一个且只有一个丈夫
  4. 每个丈夫必须有一个且只有一个妻子
  5. 到目前为止,我已在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 个答案:

答案 0 :(得分:3)

可延迟约束的替代方案是(丈夫,妻子)的第三个表,其具有两个唯一约束(一个关于丈夫,一个关于妻子),并且在该关系与丈夫表和妻子表之间具有参照完整性约束。丈夫/妻子表上的妻子/丈夫专栏将是多余的,应该被删除。

PS。它应该是WIVES而不是WIFES吗?

答案 1 :(得分:3)

使用可延迟约束的需要通常是指向设计问题的指针。当然这个数据模型不是很好:它没有正确规范化。标准化解决方案如下所示:

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>

对于那些认为用实施技巧解决数据建模问题而作弊的人,我恳求“罪名成立”,但我已经度过了漫长而艰难的一天。

答案 2 :(得分:2)

研究可延迟的约束(不是新类型,只是现有类型的参数),到目前为止你做得很好。

答案 3 :(得分:1)

延迟约束是正确的方法。有趣的是,有一种替代方法 - 使用您的设置和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

我不喜欢将事务逻辑放入触发器中,但如果您遵循此路径,则不需要可延迟的约束。

答案 4 :(得分:1)

愚蠢的想法 - 为什么不只是有一个表“夫妻”与“Husband_Name”和“Wife_Name”列,每个都有一个独特的约束?在我看来这样满足所有要求。 :)

答案 5 :(得分:0)

1)setAutoCommit()为false 2)将记录插入一个工作单元的两个表中。 3)提交

答案 6 :(得分:0)

你需要第三张桌子,不仅是为了解决这个问题,而且还要妥善处理在世界40多个国家合法的一夫多妻/重婚,

答案 7 :(得分:0)

对不起 - 大多数答案都没有解决手头的确切问题:

“必须只有一个”

这实质上意味着:你不能将一个人插入数据库!!! *因为一个人不会只有一个合伙人!

所以唯一有效的解决方案是:

  1. 可延迟的约束:尽可能简单 - 只需标记你的约束条件,然后插入一个妻子和一个丈夫,它只会在提交后检查完整性(我不知道人们在抱怨什么 - 这个不是作弊或奇怪......在许多商业案例中这是常见做法和唯一途径 !!!)

  2. INSERT ALL - 至少在较新的Oracle版本中,您可以使用INSERT ALL语句,它将一次插入多个表中。所以你可以写一个单独的“插入妻子和hsuband”,这是许多用例的可能性。

  3. 触发器:在这种特殊情况下,触发器会起作用 - 但只要你有其他属性就不再有效了......

  4. 但所有其他答案对于提出的问题都是错误的:两个具有强制 1对1连接的对象