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

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

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

  1. 用户小部件1:用户WidgetID (PK)、WidgetID (FK)、用户ID (FK)
  2. 用户小部件2:WidgetID(PK,FK),用户ID(PK,FK)

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

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

有帮助吗?

解决方案

无论哪种情况,您都只有一个主键。第二个是所谓的复合键。引入新专栏没有充分的理由。实际上,您必须在所有候选键上保留唯一索引。添加新色谱柱只会增加维护费用。

选择选项 2。

其他提示

就我个人而言,我 由于以下原因,多对多表中具有合成/代理键列:

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

合成密钥不能替代自然/复合密钥,也不能成为 PRIMARY KEY 对于该表只是因为它是表中的第一列,所以我部分同意 Josh Berkus 的文章。然而,我不同意自然键总是好的候选者 PRIMARY KEY's 如果要将它们用作其他表中的外键,当然不应该使用它们。

选项 2 使用简单的复合键,选项 1 使用 代理键. 。选项 2 在大多数情况下是首选,并且接近逻辑模型,因为它是一个很好的候选键。

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

  1. 随着时间的推移,您并不认为复合键是一个好的候选键。特别是对于时态数据(随时间变化的数据)。如果您想向 UserWidget 表添加具有相同 UserId 和 WidgetId 的另一行该怎么办?想想就业(EmployeeId,EmployeeId) - 它在大多数情况下都有效,除非有人稍后再为同一雇主工作
  2. 如果您正在创建消息/业务交易或类似的内容,需要更简单的密钥才能用于集成。也许是复制?
  3. 如果您想创建自己的审核机制(或类似机制)并且不希望密钥变得太长。

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

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

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

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

在这种情况下主键有什么好处?考虑没有主键的选项:用户小部件3:小组件 ID (FK)、用户 ID (FK)

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

拥有主键通常的性能优势是您经常通过主键查询表,速度很快。对于多对多表,您通常不会通过主键进行查询,因此没有性能优势。多对多表是通过外键查询的,因此您应该考虑在 WidgetID 和 UserID 上添加索引。

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

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

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

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

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

我会两者都去。

听我说:

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

然而:除非您使用单个生成的主键(代理键),否则我在使休眠正常工作时遇到了各种各样的麻烦。

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

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

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

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

所以:

userwidgets( widgetid(fk), userid(fk),
             unique_index(widgetid, userid)
)

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top