多对多表中的一个或两个主键?

时间:2008-09-02 05:21:36

标签: database data-structures indexing schema

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

  • 小工具:WidgetID(PK),标题,价格
  • 用户:UserID(PK),FirstName,LastName

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

  1. UserWidgets1:UserWidgetID(PK),WidgetID(FK),UserID(FK)
  2. UserWidgets2:WidgetID(PK,FK),UserID(PK,FK)
  3. 选项1的主键有一列。但是,这似乎是不必要的,因为存储在表中的唯一数据是两个主表之间的关系,并且这种关系本身可以形成唯一键。因此导致选项2,其具有两列主键,但丢失了选项1具有的一列唯一标识符。我还可以选择在第一个表中添加两列唯一索引(WidgetID,UserID)。

    两种性能方面是否有任何真正的区别,或者是否有任何理由更喜欢使用一种方法来构建UserWidgets多对多表?

9 个答案:

答案 0 :(得分:24)

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

使用选项2。

答案 1 :(得分:5)

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

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

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

答案 2 :(得分:5)

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

您可能希望使用代理键(选项1)

  1. 你不是说复合键随着时间的推移是一个很好的候选键。特别是对于时间数据(随时间变化的数据)。如果要使用相同的UserId和WidgetId向UserWidget表添加另一行,该怎么办?考虑就业(EmployeeId,EmployeeId) - 在大多数情况下它会起作用,除非有人在以后再次为同一雇主工作
  2. 如果您要创建消息/业务事务或类似的东西,需要更简单的密钥来用于集成。复制可能吗?
  3. 如果您想创建自己的审核机制(或类似),并且不希望密钥过长。
  4. 根据经验,在建模数据时,您会发现大多数关联实体(多对多)是事件的结果。人员占用就业,项目被添加到篮子等。大多数事件都对事件具有时间依赖性,其中日期或时间是相关的 - 在这种情况下,代理键可能是最佳选择。

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

答案 3 :(得分:3)

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

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

答案 4 :(得分:2)

此方案中主键的好处是什么?考虑没有主键的选项:   UserWidgets3:WidgetID(FK),UserID(FK)

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

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

答案 5 :(得分:2)

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

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

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

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

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

答案 6 :(得分:1)

我会同时选择。

听我说:

复合键显然是一种很好的,正确的方法,可以反映出数据的含义。没问题。

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

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

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

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

所以:

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

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