【问题标题】:In a junction table, should I use a Primary key and a unique constraint, or a compound/composite key?在联结表中,我应该使用主键和唯一约束,还是复合/复合键?
【发布时间】:2015-03-03 23:22:28
【问题描述】:

我已经阅读了一些似乎使这个问题重复的内容。但是通读所有这些让我不确定。我希望根据下面的绝对示例得到答案,因为许多问题/答案都在来回辩论中逐渐消失。

如果我有:

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

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

现在对于简单的联结表,我有两种选择:

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

以上将是两个 FK 上的复合/复合键,以及为两列设置 FK 关系 - 删除时也使用 Cascade。

或

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

BookID 和 AuthorID 上的外键关系,以及删除时的 Cascade。还要对 BookID 和 AuthorID 设置唯一约束。

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

这个话题似乎被撕成了两半,但我的直觉告诉我,我应该只使用复合键。

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

【问题讨论】:

  • 一位编辑提出的奇怪案例是一本书,由不同作者提交的章节组成,每章可能有几个。正如 Joel Coehoorn 指出的那样,可能还有另一个实体需要跟踪:章节。
  • @HABO 对,很好。到目前为止,每个答案似乎都是正确的,但在我的示例中,永远不会有另一列。

标签: sql-server tsql compound-key


【解决方案1】:

这是我在大学时的数据库课程中一直记得的东西。我们涵盖了教科书中关于“实体设计”的部分,它正在谈论连接表……我们称它们为相交表或相交关系。那天我在课堂上其实很专心。教授说,根据他的经验,多对多联结表几乎总是表示一个未识别的缺失实体。这些实体几乎总是以自己的数据结束。

我们得到了Student 和Course 实体的示例。要让学生参加课程,您需要在这两者之间建立联系。结果,您实际拥有的是一个新实体:Enrollment。在这种情况下,附加数据将是诸如信用类型(审核与常规)或最终成绩之类的内容。

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

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

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

【讨论】:

  • fwiw,我相信这门课应该是 2001 年春季,所以我们在 14 年前就讨论过这个问题。
  • 关于需要注意其他属性的明智点。您假设的 Enrollment 表是许多人所说的“连接表”,尽管该术语似乎很少被精确定义。如果术语联结表意味着任何东西,那么它可能意味着具有多个外键的表。我个人认为该术语不是很有用。没有联结表,只有表。
  • @JoelCoehoorn 我几乎不记得上周了,更不用说 2001 年了。干得好 :)
【解决方案2】:

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

您的第一个提案仍然获得了这个好处,但在每个索引级别上的 fan-put 也增加了 50%,从而减少了连接表上索引的整体大小和高度。尽管对于比大型表更小的任何东西来说,这样做的性能优势可能可以忽略不计,但它是最佳实践并且是免费的。

如果关系要增加额外的列,我可能会选择其他设计。到那时,它不再是严格意义上的连接表。

【讨论】:

  • 我的理解是,如果你有一个复合键,那么复合键中出现的列的顺序很重要。当您尝试在该对中的第二列上查找(认为加入)时,效率非常低,因为物理顺序(在集群 PK 的情况下)或索引(在非集群 PK 的情况下)不允许高效查找。因此,无论如何您最终都会在第二列上分别创建索引。这感觉真的是不必要的不​​对称。
  • @zespri:它可以通过第二个键上的非聚集索引来修复。如果您使用 SQL Server 或其他允许您在索引中包含非键列的数据库,您也可以包含第一个键,然后您基本上会有两个表,一个用于从任一方向查找(计数索引基本上是一个表,它有点像)。
  • 我应该在合并的列上创建索引,如列中:AuthorID (ASC)、BookID (ASC),还是在每个列上创建一个单独的索引?还是会更频繁地查询哪个多对多方向的问题?
  • @user1447679:您需要索引之一来强制执行连接表的 all-key 属性(可能是为了实现约束而创建的),以便按(预期) 最频繁的查找。稍后,以相反的方式添加一个非聚集索引根据性能需要。
  • @PieterGeerkens 使用表设计器,例如,在 SQL Server 中,选择两个列并选择“设置主键”默认情况下会在列组合上创建一个聚集索引。因此,在这种情况下,您是说我应该在将更频繁地加入/查询的列上再添加一个非聚集索引,如果性能需要,也许稍后再为另一列添加另一个索引?只是确保我理解。
【解决方案3】:

我更喜欢第一个设计,使用复合键。即使父表有它们,在联结表上有一个标识列也不会给您带来优势。您不会使用标识列查询BookAuthor,而是使用BookID 和AuthorID 查询它。

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

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

【讨论】:

    【解决方案4】:

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

    更少的存储开销

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

    所以最终结果是:

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

    查询联结表引用表

    即使您要添加像 Contributions (AuthorId, BookId, ...) 这样的表来引用联结表。大多数查询根本不需要接触联结表。例如:查找特定作者的所有贡献只涉及作者和贡献表。

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

        您可能应该使用复合/复合键。这样您就可以完全建立关系 - 一位作者可以写多本书,而一本书可以有多个作者。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-10-06
          • 2015-06-23
          • 1970-01-01
          • 1970-01-01
          • 2011-01-23
          • 1970-01-01
          • 2018-07-29
          • 1970-01-01
          相关资源
          最近更新 更多