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

Database 多对多表中的一个或两个主键?,database,data-structures,indexing,schema,Database,Data Structures,Indexing,Schema,我的数据库中有以下具有多对多关系的表,这些表由一个连接表表示,该连接表具有每个主表的主键的外键: Widget:WidgetID(PK)、标题、价格 用户:UserID(PK)、FirstName、LastName 假设每个用户小部件组合都是唯一的。对于如何构造定义数据关系的连接表,我可以看到两个选项: UserWidgets1:UserWidgetID(PK)、WidgetID(FK)、UserID(FK) UserWidgets2:WidgetID(PK,FK),UserID(PK,FK

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

  • Widget:WidgetID(PK)、标题、价格
  • 用户:UserID(PK)、FirstName、LastName
假设每个用户小部件组合都是唯一的。对于如何构造定义数据关系的连接表,我可以看到两个选项:

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


    这两种性能方法之间是否存在真正的差异,或者是否有任何理由选择一种方法而不是另一种方法来构建UserWidgets多对多表?

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

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


    选择选项2。

    不需要第一个表中的userwidgetid,正如您所说,唯一性来自widgetid和userid的组合

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

    因此:

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

    在这个场景中主键的好处是什么?考虑没有主键的选项: UserWidgets3:WidgetID(FK),UserID(FK)

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


    拥有主键通常的性能优势是,您经常通过主键查询表,这很快。对于多对多表,通常不按主键进行查询,因此没有性能优势。许多对多表都是用外键查询的,所以您应该考虑在WiGeTeD和USER ID上添加索引。< /P> < P>选项2是正确的答案,除非您有一个很好的理由来添加代理数字键(您在选项1中已经完成了)。 代理数字键列不是“主键”。从技术上讲,主键是唯一标识表中记录的列组合之一

    任何构建数据库的人都应该阅读Josh Berkus的这篇文章,以了解代理数字键列和主键之间的区别

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


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

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

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

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

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

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

    选项2使用简单的计算键,选项1使用外键。选项2在大多数情况下都是首选方案,并且与LReation模型接近,因为它是一个很好的候选关键点

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

  • 随着时间的推移,您不会发现复合密钥是一个很好的候选密钥。特别是时间数据(随时间变化的数据)。如果您想使用相同的UserId和WidgetId向UserWidget表添加另一行,该怎么办?想想就业(EmployeeId,EmployeeId)——在大多数情况下都是有效的,除非有人回去工作 userwidgets( widgetid(fk), userid(fk), unique_index(widgetid, userid) )