【问题标题】:SQL: Do you need an auto-incremental primary key for Many-Many tables?SQL:您是否需要多对多表的自动增量主键?
【发布时间】:2010-10-21 21:05:11
【问题描述】:

假设您在 Artists 和 Fans 之间有一个 Many-Many 表。在设计桌子的时候,你是不是这样设计桌子的:

ArtistFans
    ArtistFanID (PK)
    ArtistID (FK)
    UserID (FK)

 (ArtistID and UserID will then be contrained with a Unique Constraint 
  to prevent duplicate data) 

或者您是否为两个相关字段构建使用复合 PK:

ArtistFans
    ArtistID (PK)
    UserID (PK)

(The need for the separate unique constraint is removed because of the 
 compound PK)

使用以前的模式有什么优势(可能是索引?)?

【问题讨论】:

  • 警告:如果没有特定的数据库标签,这里所说的大部分内容都是可疑的。不同的引擎工作方式不同!

标签: sql database-design


【解决方案1】:
ArtistFans
    ArtistID (PK)
    UserID (PK)

使用自动增量 PK 在这里没有任何优势,即使父表有它们。

我还会在 (UserID, ArtistID) 上自动创建一个“反向 PK”索引:您将需要它,因为您将按两列查询表。

自动编号/ID 列有自己的位置。在基于物理平台的标准化过程之后,您会选择它们来改进某些事情。但不适用于链接表:如果您的死脑筋 ORM 坚持,那么更改 ORM...

编辑,2012 年 10 月

请务必注意,您仍然需要唯一的 (UserID, ArtistID)(ArtistID, UserID) 索引。添加自动增量只会使用更多不应使用的空间(在内存中,而不仅仅是在磁盘上)

【讨论】:

  • 主键只是一种特殊类型的索引。复制主键的索引只会增加开销。
  • 顺序很重要:这是一个不同的索引
  • 从一个用户钻到另一个艺术家以得到相反的结果(也就是一个用户的所有艺术家)是很重要的。使用反向索引,它允许索引查找而不是 PK 扫描。对于“每个艺术家的用户数”查询来说,PK 就足够了:但反之则不行。
【解决方案2】:

假设您已经是代理键的忠实拥护者(您有很好的伙伴),那么就有理由一路走下去。

有时会忘记的一个关键点是关系本身可以具有属性。通常只说两件事是相关的是不够的;您可能必须描述这种关系的性质。换句话说,关系表说它只能有两列并没有什么特别之处。

如果这些表没有什么特别之处,为什么不像对待其他所有表一样对待它并使用代理键呢?如果您最终不得不向表中添加属性,您将感谢您的幸运表示层,您不必为了修改这些属性而传递复合键。

我什至不会将此称为经验法则,更多的是要考虑的事情。根据我的经验,极少数的关系最终会携带额外的数据,本质上本身就是实体,值得一个代理键。

问题在于,事后添加这些键可能会很痛苦。额外的列和索引的成本是否值得先发制人,这真的取决于项目。

至于我,一次被咬,两次害羞——我去寻找门外的代理钥匙。

【讨论】:

  • 非常感谢您的评论。我对此感到非常孤独。我完全同意你的论点。
【解决方案3】:

即使您创建了一个标识列,它也不必是主键。

ArtistFans
    ArtistFanId
    ArtistId (PK)
    UserId (PK)

标识列可用于将此关系与其他关系相关联。例如,如果有一个创建者表指定了创建艺术家-用户关系的人,那么它可能在 ArtistFanId 上有一个外键,而不是组合的 ArtistId+UserId 主键。

此外,某些 ORM 包还需要标识列(或大大改进其操作)。

【讨论】:

  • 这意味着要找到创作者-艺术家的详细信息,您总是必须在 JOIN 中包含 ArtistFans 表 = 更慢。
  • 在这种情况下,您似乎不太可能对艺术家-用户关系的创建者感兴趣,而不是对用户感兴趣。
【解决方案4】:

我想不出任何理由使用您列出的第一个表格。复合主键很好,拥有一个单独的人工主键(以及您在外键上需要的唯一约束)只会花费更多时间来计算和存储空间。

【讨论】:

    【解决方案5】:

    标准方式是使用复合主键。添加单独的自动增量键只是使用您拥有的东西创建一个已经存在的替代品。正确的数据库规范化模式会忽略使用自动增量。

    【讨论】:

      【解决方案6】:

      有趣的是,所有答案都支持变体 2,所以我不得不反对并支持变体 1 ;)

      回答标题中的问题:不,你不需要它。但是……

      每个表中都有一个自动增量或标识列可以简化您的数据模型,以便您知道每个表始终有一个 PK 列。

      因此,从一个表到另一个表的每个关系(外键)总是由每个表的单个列组成。

      此外,如果您碰巧为表单、列表、报告、日志记录等编写了一些应用程序框架,您只需处理具有单个 PK 列的表,这会简化框架的复杂性。

      另外,额外的 id PK 列在磁盘空间方面不会花费您太多(十亿记录以上的表除外)。

      当然,我需要提到一个缺点:在祖父母-孩子关系中,孩子会丢失其祖父母信息并需要 JOIN 来检索它。

      【讨论】:

      • ...以及许多唯一索引(唯一约束也是磁盘上的索引)以确保您的自然键的数据完整性...
      • 我不同意添加额外的列会使事情变得更简单。并且使用自动增量代理键代替自然 PK 会使数据完整性变弱,除非您添加额外的约束,正如 devio 评论的那样。如果分析得好,实体表将有一个简单的 PK,而关系表将有一个复合 PK。还有什么比这更简单的?
      【解决方案7】:

      在我看来,在纯 SQL 中 id 列是没有必要的,也不应该使用。但是对于像 Hibernate 这样的 ORM 框架,使用复合键等管理多对多关系并不简单,尤其是在连接表有额外列的情况下。

      因此,如果我要在数据库上使用 ORM 框架,我更喜欢将自动增量 id 列放在该表中,并将唯一约束放在引用列中。当然,如果需要,也可以使用非空约束。

      然后我像对待项目中的任何其他表一样对待该表。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-07
        • 1970-01-01
        • 2011-09-03
        • 2012-06-21
        • 1970-01-01
        • 2011-01-12
        • 1970-01-01
        相关资源
        最近更新 更多