映射到多个主键的外键列

Posted

技术标签:

【中文标题】映射到多个主键的外键列【英文标题】:Foreign Key column mapped to multiple primary keys 【发布时间】:2014-03-04 16:40:36 【问题描述】:

我有一个包含三个表的数据库

消息 - PK = MessageId 草稿 - PK = DraftId 历史 - FK = RelatedItemId

History 表有一个外键 [RelatedItemId],它映射到 MessagesDrafts 中的两个主键之一。

这种关系有名称吗?

这只是糟糕的设计吗?

有没有更好的方法来设计这种关系?

以下是该问题的 CREATE TABLE 语句:

 CREATE TABLE [dbo].[History](
    [HistoryId] [uniqueidentifier] NOT NULL,
    [RelatedItemId] [uniqueidentifier] NULL,
    CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED ( [HistoryId] ASC )
 )

CREATE TABLE [dbo].[Messages](
    [MessageId] [uniqueidentifier] NOT NULL,
    CONSTRAINT [PK_Messages] PRIMARY KEY CLUSTERED (    [MessageId] ASC )
 )


CREATE TABLE [dbo].[Drafts](
    [DraftId] [uniqueidentifier] NOT NULL,
    CONSTRAINT [PK_Drafts] PRIMARY KEY CLUSTERED (  [DraftId] ASC )
)

【问题讨论】:

最好提供CREATE 声明。在某些 rdbms 中,您可以像 SHOW CREATE TABLE Messages; 一样检索它 我发现很难选择答案,因此掷骰子... 【参考方案1】:

简而言之,您使用的解决方案称为:多态关联 目标:引用多个父母结果反-pattern: 使用双重用途外键,违反第一范式(原子问题),失去参照完整性 解决方案: 简化关系 More information about the problem .

顺便说一句,创建一个通用的超级表将帮助您:

【讨论】:

【参考方案2】:

这种关系有名称吗?

我知道没有标准名称,但我听说有人使用“通用 FK”甚至“inner-platform effect"”这个词。

这只是糟糕的设计吗?

是的。

原因:它阻止您声明 FOREIGN KEY,因此阻止 DBMS 直接强制执行参照完整性。因此,您必须通过命令式代码(surprisingly difficult)强制执行它。

有没有更好的方法来设计这种关系?

是的。

为每个引用的表创建单独的 FOREIGN KEY。使它们可以为 NULL,但通过 CHECK 约束确保其中一个是非 NULL。

或者,看看inheritance。

【讨论】:

Talspaugh27 的回答是否消除了您对参照完整性的担忧? @bnieland 好吧,它允许两个连接,您特别指出它“映射到一个”。但即使假设这是固定的,我也看不出它会如何避免我在链接中提到的竞争条件。它是特定于 DBMS 的(不仅仅是在语法上,一些 DBMS 根本不支持引用其他行或表的 CHECK)。而且它的性能可能不如“原生”FK。并且不那么明显和自我记录。好的,我现在停止抨击它;) @bnieland 话虽如此,仔细实现类似的想法(避免竞争条件)可以为您节省一堆 NULL,但无论如何 NULL 往往很便宜(尤其是在 MS SQL Server 下, Oracle 略少,YMMV)。【参考方案3】:

我发现的最佳实践是创建一个函数,返回传入的值是否存在于您的 Messages 和 Drafts PK 列中。然后,您可以在调用此函数的 History 列上添加一个约束,并且只有在它通过时才会插入(即它存在)。

添加未解析的示例代码:

CREATE FUNCTION is_related_there ( IN @value 唯一标识符) 返回小音 开始 IF (从 DraftId = @value 的草稿中选择 count(DraftId) + 从 MessageId = @value 的消息中选择 count(MessageId) > 0 THEN 返回 1; 别的 返回 0; 万一; 结束;

ALTER TABLE 历史记录添加约束 CK_HistoryExists CHECK (is_related_there (RelatedItemId) = 1)

希望运行和帮助大声笑

【讨论】:

一个很好的答案,我希望看看其他人的想法! 它实际上是一种用于在以数据为中心的架构中处理多级继承的方法。已经看到它在 Java 和 .net 应用程序中使用过

以上是关于映射到多个主键的外键列的主要内容,如果未能解决你的问题,请参考以下文章

问题 Datagridview 和外键和主键列

hibernate笔记--基于主键的单(双)向的一对一映射关系

SQLAlchemy 一个映射类中的多个外键到同一个主键

当主键列是mysql中不同表的外键时,如何将主键列更改为自动递增

EntityFramework db.where()查询符合条件数据主键的外键的外键 在括号里怎么写

请问SQL server 中的主键和外键的作用