【问题标题】:What are the pros and cons of using multi column primary keys?使用多列主键的优缺点是什么?
【发布时间】:2010-02-05 00:58:22
【问题描述】:

我想看一个例子:

  • 什么时候合适
  • 当这不合适时

数据库的选择是否会对上述示例产生影响?

【问题讨论】:

    标签: sql primary-key composite-primary-key


    【解决方案1】:

    这似乎真的是一个关于 代理 键的问题,它们始终是自动递增的数字或 GUID,因此是单列,而不是 自然 键,这通常需要多条信息才能真正独一无二。如果您能够拥有一个只有一列的自然键,那么这一点显然没有实际意义。

    有些人会坚持只使用其中一种。花足够的时间使用生产数据库,您会发现没有与上下文无关的最佳实践。

    其中一些答案使用 SQL Server 术语,但这些概念通常适用于所有 DBMS 产品:


    使用单列代理键的原因:

    • 聚集索引。当数据库只能追加到聚集索引时,聚集索引总是表现最好 - 否则,数据库必须执行page splits。请注意,这仅适用于键是 sequential 的情况,即自动递增序列或顺序 GUID。任意 GUID 的性能可能会更差。

    • 关系。如果您的键长度为 3、4、5 列,包括字符类型和其他非紧凑数据,您最终会浪费 大量如果您必须在其他 20 个表中创建与该键的外键关系,则会降低空间并随后降低性能。

    • 唯一性。有时您没有真正的自然键。也许你的表是某种日志,你有可能同时得到两个相同的事件。或者,也许您的真正密钥类似于物化路径,只能在已插入行之后 确定。无论哪种方式,您总是希望您的聚集索引和/或主键是唯一的,因此如果您没有其他真正唯一的信息,您别无选择,只能使用代理键。

    • 兼容性。大多数人永远不必处理这个问题,但如果自然密钥包含类似hierarchyid 的内容,则某些系统可能甚至无法读取它。在这种情况下,您再次必须创建一个简单的自动生成的代理键供这些应用程序使用。即使自然键中没有任何“奇怪”的数据,一些数据库库在处理多列主键时也会遇到很多麻烦,尽管这个问题很快就会消失。

    使用多列自然键的原因

    • 存储。 许多使用数据库的人从来没有使用过足够大的数据库,因此不得不关心这个因素。但是,当一个表有数十亿或数万亿行时,您将希望在该表中保留尽可能少的数据量。

    • 复制。是的,您可以使用 GUID 或顺序 GUID。但是 GUID 有其自身的权衡,如果由于某种原因您不能或不想使用 GUID,多列自然键是复制方案的更好选择,因为它本质上是全局的唯一的 - 也就是说,您不需要特殊的算法来使其唯一,它是唯一的定义。这使得推理分布式架构变得非常容易。

    • 插入/更新性能。代理键不是免费的。如果您有一组唯一的列并且经常被查询,因此您需要在这些列上创建覆盖索引;索引最终几乎与表一样大,这会浪费空间并且需要在每次进行任何修改时更新第二个索引。如果您有可能在一个表上只有 一个 索引(聚集索引),那么您应该这样做!


    这就是马上想到的。如果我突然想起什么,我会更新。

    【讨论】:

    • 简短而有帮助。我正在使用自然键,并且我大多数时候都避免使用代理键,但是我需要一个表来存储用户日志,并且在阅读了您有用的答案后,我决定使用它。谢谢大佬。
    【解决方案2】:

    我认为(至少从应用程序开发人员的角度来看)使主键成为自动生成的键,并在多列上创建唯一约束和索引几乎总是更好。

    • 使用一个自动生成的主键,您就可以轻松地从其他表中添加对该表的引用。
    • 自动生成的主键与 ORM 库一起使用更简单。
    • 另外,如果您的唯一性约束在未来发生变化,您不必更改现有的主键。

    我遇到过一些令人头疼的情况,因为 DBA 认为多列主键总是足够的,而未来的需求变化证明这是不正确的。

    【讨论】:

    • 关于未来验证代码的要点。支持未来变化的灵活性很重要。
    【解决方案3】:

    您几乎总是需要一个主键,所以我假设选择是选择现有的两列作为主键,或者创建一个新的自动递增 PK 并在两列上放置一个普通的唯一约束。

    当你想要一个 2 列的主键时:

    • 如果您有一个引用其他两个表的中间表并且它只包含两个外键,即多对多关系,那么添加一个额外的列只是作为主键是没有意义的。使用您已有的两列作为主键。

    当你想要一个自增主键时:

    • 如果您从另一个表中引用一个表,您希望目标表的主键较小,因为该数据将作为引用表中的外键重复。您还希望它能够快速进行比较。
    • 您添加到表中的每个索引都包含集群键的副本(通常与主键相同)。如果您的集群键大于所需大小,则该表上的每个索引也将大于所需大小。

    【讨论】:

    • 您添加到表中的每个索引都包含主键的副本。如果您的主键大于所需大小,则该表上的每个索引也将大于所需大小。 没有主键引用的表上的索引是什么?你知道这是真的,还是只是这么假设?
    • 每个索引都包含集群键,它可能是也可能不是主键(通常是)。
    • 如果在主键之前创建索引会怎样?我认为给定一个索引指向一行,但是是什么让您认为行指针只是主键?主键索引指向什么?我认为这种说法根本不正确。
    • @Evan:如果您没有聚集键(同样,它可能是也可能不是主键),那么每个非聚集索引都包含一个空副本,因此该陈述仍然正确。 ;)
    • @Aaronaught:关于集群键与主键的好点,你完全正确 - 我在这里做了一个可能不正确的假设。
    【解决方案4】:

    几个例子……

    适当的:

    • OLTP 系统,在大多数情况下实现大多数多对多关系时。

    不当:

    • 对于 OLAP 系统中的维度表 - 您希望维度键尽可能小,以便事实表尽可能小(且快)。

    • 当您不确定组合是否唯一时。诚然,这是一个非常糟糕的示例,但对于多列 PK,“Person”表将是一个糟糕的选择。

    【讨论】:

      【解决方案5】:

      一个合适的例子是当你有一个链接表,其中外键字段连接不同的表。

      一般来说,尽可能使用现有的标识字段作为主键可能是个好主意。如果您没有自然的 id 字段,并且您必须组合很多字段才能获得唯一的 PK,那么使用自动编号可能会更好。超过 2 个字段的主键可能会变得混乱。

      【讨论】:

        【解决方案6】:

        我们发现,当我们使用多列索引和键时,我们的应用程序的性能得到了极大的提升。它允许我们在最常见的查询上创建索引,并且甚至不访问主表,因为整个 select 子句可能在索引中。但是,这取决于您的应用和数据集。

        【讨论】:

        • 请注意,这不应被视为所有数据库的一般建议。例如,只有在查询中使用了索引中的所有列时,才会使用 Teradata 上的多列索引,因为 Teradata 使用哈希进行索引。
        • 是的,这是在具有数亿行的企业系统上。这就是为什么我声明我们的应用程序适用于大多数应用程序,您可能不会获得我们所拥有的好处。我们的索引由 IBM 的 DB2 工程师调整以获得最大收益。
        • 是的,但是如果您有一个五列的主键,那么来自子表的任何 JOIN 都将是一团糟!它需要五个条件才能建立 JOIN..... JOINs from HELL!
        • marc_s:您假设在这些子表中使用了相关键。不一定是这样。 key 和 foreign key 的意思不一样。
        【解决方案7】:

        有时复合自然键具有直观意义。例如。假设您有一个公司表(PK 是 ComapnyId),列中有公司的一些详细信息。您还需要在公司的整个历史中存储公司的 CEO 姓名。自然不变的是,一家公司一次只能有一位 CEO。然后使用 CompanyId(Company 表中 CompanyId 的 FK)+ FromDate 的复合 PK 创建一个 CompanyCeo 表是很直观的。该表中的其他列可能是 ToDate 和 CeoName。这样您就可以保证只有一位 CEO 可以在特定日期开始工作。

        【讨论】:

          猜你喜欢
          • 2010-09-08
          • 1970-01-01
          • 1970-01-01
          • 2010-09-06
          • 1970-01-01
          • 2015-01-30
          • 2011-04-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多