在联结 table 中,我应该使用主键和唯一约束,还是应该使用 compound/composite 键?

In a junction table, should I use a Primary key and a unique constraint, or a compound/composite key?

我已经阅读了一些似乎使这个问题重复的内容。但是通读所有这些让我不确定。我希望根据下面的 absolute 示例得到答案,因为许多 questions/answers 来回争论不休。

如果我有:

dbo.Book
--------
BookID PK int identity(1,1)

dbo.Author
----------
AuthorID PK int identity(1,1)

现在我有两个简单连接的选择table:

dbo.BookAuthor
--------------
BookID CPK and FK
AuthorID CPK and FK

以上将是两个 FK 上的 compound/composite 键,以及为两列设置 FK 关系 - 也使用级联删除。

dbo.BookAuthor
--------------
RecordID PK int identity(1,1)
BookID FK
AuthorID FK

BookID 和 AuthorID 的外键关系,以及删除时的级联。同时对 BookID 和 AuthorID 设置唯一约束。

我正在寻找一个简单的答案,说明为什么在上述特定示例中一种方法优于另一种方法。我正在阅读的答案非常详细,我正要确定一个复合键,但后来观看了一个视频,其中的示例使用了标识列,就像我的第一个示例一样。

看来这个主题有点被撕成两半,但我的直觉告诉我,我应该只使用复合键。

查询效率更高的是什么?似乎有一个 PK 标识列以及在两列上设置唯一约束,并且 FK 关系会更昂贵,即使是一点点。

我更喜欢第一种设计,使用复合键。在联结点 table 上有一个标识列不会给你带来优势,即使父 table 有它们。您不会使用标识列查询 BookAuthor,而是使用 BookIDAuthorID.

查询它

此外,添加身份将允许重复 BookID-AuthorID 组合,除非您设置约束。

此外,如果您的主键是 (BookID, AuthorID),您需要在 AuthorID, BookID) 上建立索引。如果您想查询作者所写的书籍,这将有所帮助。

作为代理键的好处的坚定支持者和宣传者,我 none-the-less 对全键连接 table 作为例外,例如您的第一个示例.代理键的好处之一是引擎通常针对连接单个整数字段进行了优化,这是默认和最常见的情况。

您的第一个提案仍然获得此优势,但每个索引级别的粉丝投入也增加了 50%,从而减少了连接 table 上索引的总体大小和高度。虽然对于任何小于大型 table 的东西来说,这样做的性能优势可能可以忽略不计,但这是最佳实践,而且是免费的。

当我可能选择其他设计时,如果关系要产生额外的列。那时它不再是严格的连接 table.

您可能应该使用 compound/composite 键。这样你就完全相关了——一个作者可以写很多书,一本书可以有多个作者。

这是我在大学数据库课程中一直记得的事情。我们覆盖了“实体设计”教科书中的部分,它正在谈论交汇点 tables ......我们称它们相交 tables 或相交关系。那天我实际上是在 class 关注的。教授说,根据他的经验,多对多连接 table 几乎总是表示身份不明的缺失实体。 这些实体几乎总是以自己的数据结束。

我们得到了 StudentCourse 实体的示例。对于一个学生要上一门课程,你需要在这两者之间建立联系。结果,您实际拥有的是一个新实体:Enrollment。在这种情况下,附加数据将是诸如信用类型(审计与常规)或最终成绩之类的东西。

直到今天我还记得那个建议......但我并不总是遵循它。在这种情况下,我将做的是停止,并确保就此问题回到利益相关者那里,并与他们一起研究我们在这个交界处可能仍然缺少哪些数据点。如果我们真的找不到任何东西,那我就用复合键。当我们找到数据时,我们会想到一个更好的名字,它会得到一个代理键。

2020年更新
我仍然保留着这本教科书,令人惊讶的是,它和这个问题在几个小时内就引起了我的注意。所以出于好奇,这是本书第 7 版的第 5 章第 6 节:

https://www.amazon.com/Database-Processing-Fundamentals-Design-Implementation-dp-9332549958/dp/9332549958/

使用复合键也是我的选择。原因如下:

更少的存储开销

假设您将使用代理键。由于您可能想要查询特定书籍的所有作者,反之亦然,因此您需要以 BookId 和 AuthorId 开头的索引。出于性能原因,您应该在两个索引中包含另一列以防止聚集键查找。您可能希望将其中之一设为唯一,以确保没有重复的 BookId/AuthorId 组合添加到 table.

最终结果:

  • 数据存储了3次而不是2次
  • 要验证 2 个唯一约束而不是 1 个

查询联结 table 引用 table

即使您要添加一个 table,如 Contributions (AuthorId, BookId, ...) 引用联结点 table。大多数查询根本不需要触及连接点 table。例如:查找特定作者的所有贡献将只涉及作者和贡献 tables.

根据联结 table 中的数据量,复合键可能最终导致性能低于自动生成的 顺序 主键。

主键是table的聚集索引,这意味着它决定了行在磁盘上的存储顺序。如果主键的值不是按顺序生成的(例如,它是由来自 tables 的外键组成的复合键,其中行的顺序与联结点 table 的行的顺序不同,或者它是 GUID 或其他随机密钥),那么每次将行添加到联结点 table 时,都需要对联结点 table 的行进行重新洗牌。