引用具有多个外键的两列主键

Posted

技术标签:

【中文标题】引用具有多个外键的两列主键【英文标题】:Referencing a two column primary key with multiple foreign keys 【发布时间】:2013-01-27 02:28:34 【问题描述】:

在Oracle中取以下两张表:

Create Table A
( A int, B int, C int,
  Constraint pk_ab Primary Key(A, B),
  Unique (C)
);

Create Table B
( D int, E int, F int,
  Constraint fk_d Foreign Key (D) References A(A),
  Constraint fk_e Foreign Key (E) References A(B)
);

为什么这个语句不起作用?或者更具体地说,为什么它不工作?我试图创建这种关系的原因是说,将来我想删除B.D,但保留FK_E的关系。

我收到了错误:

ORA-02270: 此列列表没有匹配的唯一键或主键

【问题讨论】:

您是否收到错误消息?我不记得 Oracle 是否强制执行此操作,但如果 A.AA.B 被引用为 FK,它们可能都需要一个 individual 索引,而不仅仅是您在它们上定义的复合 PK .尝试在表A 中单独为每一列定义一个附加索引。 所以您在Table A 上的总索引是:PK (A,B), UNIQUE (C), INDEX (A), INDEX (B) 嗯,它在对我大喊:ORA-00904: : invalid identifier 哦,对不起,我没有注意到你的第一个问题,是的,我收到了错误。 ORA-02270: 此列列表没有匹配的唯一键或主键 【参考方案1】:

“为什么这个语句不起作用?或者更具体地说,为什么不应该 这行得通? "

您已将 A 上的主键定义为两列 (A,B) 的组合。任何引用 PK_AB 的外键必须在数量上与这些列匹配。这是因为外键必须标识被引用表中的单行,该表拥有子表中的任何给定行。复合主键意味着 A.A 列可以包含重复值,A.B 列也可以;只有 (A,B) 的排列是唯一的。因此,引用外键需要两列。

Create Table B
( D int, E int, F int,
  Constraint fk_de Foreign Key (D,E) References A(A,B)
);

“由于表 B 引用了多个 PK”

错了。 B 引用了一个主键,它恰好包含多个列,

" 说,将来我想删除 B.D,但保留关系 fk_e。 "

这没有意义。可以这样想:D 不是 B 的属性,它是 B 通过对表 A 的依赖而继承的属性。

避免这种情况的一种方法是使用代理(或合成)密钥。复合键通常是业务键,因此它们的列在业务上下文中是有意义的。有意义的列值的一个特点是它们可以更改,而将此类更改级联到外键可能会很麻烦。

实现代理键如下所示:

Create Table A
( id int not null, A int, B int, C int,
  Constraint pk_a Primary Key(ID),
  constraint uk_ab Unique (A,B)
);

Create Table B
( a_id int, F int,
  Constraint fk_n_a Foreign Key (A_ID) References A(ID)
);

当然,您可以使用您发布的架构来执行此操作,因为您已经在 A(C) 上有一个单列约束。但是,我认为引用唯一约束而不是主键是不好的做法,即使它是允许的。我认为这部分是因为独特的约束通常会强制执行业务密钥,因此意味着意义,因此可能会发生变化,但主要是因为引用主键只是行业标准。

【讨论】:

这正是我正在寻找的答案。详细解释了一切,谢谢!我从来没有建立一个 FK 必须识别引用表中的整个单行的连接。它还有助于弄清楚 PK 的实际含义(它不是多个键,而是构成单个键的多个属性)。再次感谢你们。【参考方案2】:

在创建表B之前,尝试为列的AB创建两个单独的索引

CREATE INDEX a_idx ON A (A);
CREATE INDEX b_idx ON A (B);

但您可能需要在桌子上使用复合 FK B

Create Table B
( D int, E int, F int,
  Constraint fk_d Foreign Key (D,E) References A(A,B)
);

【讨论】:

按原样进行复合工作。但是后来想删除B.D的问题是:Alter Table B Drop (D);导致错误。 我无法想象您为什么需要这样做。如我所说,创建单独的索引将允许您拥有单独的 FK。但可能您需要完全不同的架构设计。为了帮助您,我们需要了解您的真实需求,而不是 As 和 Bs。 哈哈,我的问题不是出于需要,而是对'为什么'的澄清说你不再需要一个属性,但它是一个外键,你会怎么做摆脱它?这是我目前的目标。 如果这是唯一的目标,请创建单独的索引。你已经有了答案。 @peterm 是的,我猜你是在祭奠彼得。今天学到了一些东西。【参考方案3】:

外键总是引用另一个表的 PK。 A 和 B 都不是 单独PK的..你有多个PK的。

Constraint fk_d Foreign Key (D,E) References A(A,B)

另外数据库也无法验证部分空的多个外键,所以我觉得表中也需要加入检查约束。

alter table B add constraint check_nullness
    check ( ( D is not null and E is not null ) or
                ( D is null and E is null ) )

【讨论】:

好的,我想我会在这里提出另一个问题。由于表B引用了多个PK,如果复合引用,如何删除表B中的外键? 我意识到它不能回答你的问题,因为你坚持拥有 2 个外键,而且你可能已经知道多 PK 的事情,并且你试图将真正只依赖于表 A.A 的实体挤入系统..我说表A PK A,表B PK B,表C PK(C,D)FK(C,D)-> PK(A,B)...现在表D FK D-> PK A... 表 E FK E -> PK B 和表 F FK (D,E) -> PK (A,B)

以上是关于引用具有多个外键的两列主键的主要内容,如果未能解决你的问题,请参考以下文章

外键可以引用具有复合(即两列的组合)键的表吗?

如何在 Sqlite 中将两列合二为一,同时获取外键的底层值?

当一个表中的两列指向另一个表中的主键时,避免循环引用

Rails 与多个外键的关联

获取具有多个枢轴的两列的总和

mysql外键仅引用复合主键的一部分