【问题标题】:One or Two Primary Keys in Many-to-Many Table?多对多表中的一个或两个主键?
【发布时间】:2010-09-07 12:30:45
【问题描述】:

我的数据库中有以下表,它们具有多对多关系,由一个连接表表示,该连接表具有每个主表的主键的外键:

  • 小部件:小部件ID (PK)、标题、价格
  • 用户:用户 ID (PK)、名字、姓氏

假设每个 User-Widget 组合都是唯一的。我可以看到关于如何构造定义数据关系的连接表的两个选项:

  1. UserWidgets1:UserWidgetID (PK)、WidgetID (FK)、UserID (FK)
  2. UserWidgets2:WidgetID(PK、FK)、UserID(PK、FK)

选项 1 有一个主键列。但是,这似乎没有必要,因为表中存储的唯一数据是两个主表之间的关系,而这种关系本身可以形成唯一键。因此导致选项 2,它具有两列主键,但丢失了选项 1 具有的一列唯一标识符。我还可以选择在第一个表中添加一个包含两列的唯一索引(WidgetID、UserID)。

两者在性能方面有什么真正的区别,或者有什么理由更喜欢一种方法而不是另一种方法来构建 UserWidgets 多对多表?

【问题讨论】:

  • 您需要的索引取决于您的查询要求,而不是您的架构设计。

标签: database data-structures indexing schema


【解决方案1】:

选项 2 使用简单的复合键,选项 1 使用 surrogate key。选项 2 在大多数情况下是首选,并且与关系模型接近,因为它是一个很好的候选键。

在某些情况下,您可能需要使用代理键(选项 1)

  1. 随着时间的推移,您不确定复合键是否是一个好的候选键。尤其是时间数据(随时间变化的数据)。如果您想在 UserWidget 表中添加具有相同 UserId 和 WidgetId 的另一行怎么办?想想就业(EmployeeId,EmployeeId) - 它在大多数情况下都可以使用,除非有人在以后为同一雇主工作
  2. 如果您正在创建消息/业务交易或类似的东西,需要使用更简单的密钥进行集成。可以复制吗?
  3. 如果您想创建自己的审计机制(或类似机制)并且不希望密钥过长。

根据经验,在对数据进行建模时,您会发现大多数关联实体(多对多)都是事件的结果。人员就业,物品被添加到购物篮等。大多数事件对事件有时间依赖性,其中日期或时间是相关的 - 在这种情况下,代理键可能是最佳选择。

所以,选择选项 2,但请确保您拥有完整的模型。

【讨论】:

    【解决方案2】:

    我会两个都去。

    听我说:

    就反映数据的含义而言,复合键显然是一种不错的、正确的方法。没问题。

    但是:除非您使用单个生成的主键 - 代理键,否则我在让 hibernate 正常工作时遇到了各种麻烦。

    所以我会使用逻辑和物理数据模型。逻辑的有复合键。实现逻辑模型的物理模型具有代理键和外键。

    【讨论】:

      【解决方案3】:

      就个人而言,我在多对多表中使用合成/代理键列,原因如下:

      • 如果您在实体表中使用了数字合成键,那么在关系表中使用相同的合成键可以保持设计和命名约定的一致性。
      • 将来可能会出现这样的情况,即多对多表本身成为需要对单个行进行唯一引用的从属实体的父实体。
      • 它不会真正使用那么多额外的磁盘空间。

      合成键不是自然/复合键的替代品,也不是该表的PRIMARY KEY,因为它是表中的第一列,所以我部分同意 Josh Berkus 的文章。但是,我不同意自然键始终是 PRIMARY KEY's 的良好候选者,如果要在其他表中用作外键,则当然不应该使用它们。

      【讨论】:

      • 我意识到这是很久以前回答的问题,但是复合键是否仍然是对父表的单个行的唯一引用(您的第 2 点)?
      • @crush - 是的,它会是独一无二的,但是在复合键上创建一个约束在不同平台上是模糊的/不一致的。我更喜欢明确和一致。每个表都有一个标识列。
      【解决方案4】:

      我同意前面的答案,但我要补充一点。 如果要向关系添加更多信息并允许相同的两个实体之间存在更多关系,则需要选项一。

      例如,如果您想跟踪用户 1 在 userwidget 表中使用小部件 664 的所有时间,则 userid 和 widgetid 不再是唯一的。

      【讨论】:

        【解决方案5】:

        选项 2 是正确答案,除非您有充分的理由添加代理数字键(您已在选项 1 中完成)。

        代理数字键列不是“主键”。主键在技术上是唯一标识表中记录的列组合之一。

        任何构建数据库的人都应该阅读这篇由 Josh Berkus 撰写的文章http://it.toolbox.com/blogs/database-soup/primary-keyvil-part-i-7327,以了解代理数字键列和主键之间的区别。

        根据我的经验,向表中添加代理数字键的唯一真正原因是,如果您的主键是复合键并且需要在另一个表中用作外键引用。只有这样,您才应该考虑在表格中添加一个额外的列。

        每当我看到每个表都有一个“id”列的数据库结构时,很可能它是由不了解关系模型的人设计的,并且总是会显示 Josh 文章中确定的一个或多个问题.

        【讨论】:

          【解决方案6】:

          在这种情况下主键有什么好处?考虑没有主键的选项: UserWidgets3:WidgetID (FK)、UserID (FK)

          如果您想要唯一性,请使用复合键 (UserWidgets2) 或唯一性约束。

          拥有主键的通常性能优势是您经常通过主键查询表,这很快。在多对多表的情况下,您通常不会按主键查询,因此没有性能优势。多对多表是通过外键查询的,所以应该考虑在WidgetID和UserID上添加索引。

          【讨论】:

            【解决方案7】:

            不需要第一个表中的userwidgetid,就像你说的唯一性来自widgetid和userid的组合。

            我会使用第二个表,保留外键并在 widgetid 和 userid 上添加唯一索引。

            所以:

            userwidgets(widgetid(fk), userid(fk), 唯一索引(小部件 ID,用户 ID) )

            没有额外的主键会有一些性能增益,因为数据库不需要计算键的索引。在上面的模型中虽然这个索引(通过unique_index)仍然是计算出来的,但是我相信这个比较容易理解。

            【讨论】:

              【解决方案8】:

              在任何一种情况下,您都只有一个主键。第二个是所谓的复合键。引入新专栏没有充分的理由。实际上,您必须在所有候选键上保留唯一索引。添加新列只会给您带来维护开销。

              选择选项 2。

              【讨论】:

              • 主键可以复合 - 术语不是排他性的。
              • @paulmurray:我相信上面的答案表明在任何一种情况下你都有一个主键,包括你有一个复合键的情况。你有什么要补充的吗?
              【解决方案9】:

              由于每个 User-Widget 组合都是唯一的,因此您应该通过使组合唯一来在表中表示它。换句话说,选择选项 2。否则,您可能有两个条目具有相同的小部件和用户 ID,但用户小部件 ID 不同。

              【讨论】:

                猜你喜欢
                • 2023-04-07
                • 2011-04-15
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-03-25
                • 2022-01-21
                相关资源
                最近更新 更多