【问题标题】:Multi column primary keys?多列主键?
【发布时间】:2008-12-08 21:21:00
【问题描述】:

例如我有 2 个表,UsersUserRelations,这是一对多的关系。

对于UserRelations 表,我可以有一个标识列并将其设为主键:

[RelationID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [int] NOT NULL,
[TargetID] [int] NOT NULL,

或者我可以像这样设计表格:

[UserID] [int] NOT NULL,
[TargetID] [int] NOT NULL,

并将UserID + TargetID 设为主键。

我的问题是,采用哪种设计对性能有更好的影响?

【问题讨论】:

  • +1 太好了,我不必浪费发布同样的问题了~

标签: database-design


【解决方案1】:

如果您使用前一种设计,带有多余的标识列,则没有限制插入具有相同 UserID 和 TargetID 的两行。您必须在其他两列上创建一个 UNIQUE 约束,这无论如何都会创建一个复合索引。

另一方面,一些框架(例如 Rails)坚持每个表都有一个名为 id 的代理键,因此“正确”的设计可能不起作用。这取决于您编写什么代码来使用此表格设计。

【讨论】:

  • 我们有时似乎是彼此的呼应。
  • 嘿!没问题,它应该对那些有类似答案的人有所帮助,并使用替代措辞。
  • 您可能需要此表的唯一外键,在这种情况下您是否仍然不创建单独的 ID 字段?
  • @brian:引用 UserRelations 表的外键也可以是复合的。
  • '另外两张表'?据推测,您的意思是“其他两列”。
【解决方案2】:

这几乎是一个宗教问题。对于每个说使用非智能代理键的人,其他人会指出代理键可能被认为是多余的,等等。所以,做任何对你和你的团队来说最舒服的事情。

如果您决定使用代理键,您还应该对自然键(在本例中为多列)设置唯一约束,以保持数据的完整性。

我通常会选择一个额外的代理键,因为自然键有时缺少许多理想的(不一定是必需的)主键特征:

  • 唯一值:主键必须唯一标识表中的每一行。
  • 非智能:主键最好没有嵌入的语义含义。换句话说,它不应该描述实体的特征。 12345 的客户 ID 通常优于 RoadWarrior
  • 随着时间的推移没有变化:主键的值通常不应该改变。更改主键值意味着您正在更改实体的身份,这通常没有意义。我更喜欢非智能键,因为它们不太可能改变。
  • 单一属性:主键应具有尽可能少的属性数。单属性主键是可取的,因为它们更易于应用程序使用,并且简化了外键的实现。
  • 数字:当它们是数字时,管理唯一值通常更容易。大多数数据库系统具有启用自动递增主键属性的内部例程。

从性能的角度来看,我怀疑在大多数情况下差别不大。但与任何性能问题一样,您应该衡量您担心的地方。

【讨论】:

    【解决方案3】:

    据我了解(实际上,在实践中它是有道理的),主键标识一些唯一数据......至少在规范化表中。如果表中有数据需要通过键更明确地标识,则应使用复合主键(其中包含多个列的主键)。

    例如,在存储当前和过去约会的表格中,客户可以多次出现在表格中,您可以像这样设置表格:

    AppointmentDate、CustomerID、AppointmentReason

    其中 AppointmentDate 和 CustomerID 是复合主键,标识 AppointmentReason 的唯一信息。

    我们使用 AppointmentDate 和 CustomerID 作为主复合键,因为多个客户可以在同一天同时进行约会。如果我们只是使用 AppointmentDate 作为主键,我们可能会遇到主键唯一性约束的问题。

    对于您的情况,了解有关将包含哪种数据的更多信息会有所帮助,但我可以将 UserID 和 TargetID 设为复合主键,同时 TargetID 作为 UserRelations 表的外键(如果出现)在您的用户表中。我会这样做,因为如果您有一个名为 RelationID 的主键,您最终会得到一个重复的 User 列,这可能会对性能产生不利影响,并且根本不会使您的表标准化。

    【讨论】:

      【解决方案4】:

      您“应该”始终尝试在每个表上建立“有意义的”或“自然的”主键或唯一索引,以帮助维护数据的完整性。当这意味着多列(或“复合”键)时,确实会影响性能 - 特别是在其他依赖表中使用与外键相同的多列键时,或在用于查询中的搜索谓词的索引等中使用相同的多列键时.

      当这些性能影响变得显着时(甚至可能在它们出现之前,您应该切换到使用无意义的“代理”键(通常是整数),用于所有这些其他目的(FK、连接、索引、查询搜索谓词、应用代码实体标识符等)

      但始终保留有意义的键或唯一索引,以维护表数据完整性

      【讨论】:

        【解决方案5】:

        就性能而言,我通过对描述关系的表使用复合主键获得了良好的结果。声明主键有两种效果:

        您得到的约束要求每个参与列都为非空,并且要求参与列的唯一性,加在一起。

        在给定主键的情况下,您将获得一个可以快速访问各个行的索引。大多数 DBMS 都会为您创建此索引。

        此索引的有用程度取决于查询优化器、主键声明中的列顺序以及数据的使用模式。有时,在复合主键中第一个以外的列上通过您自己创建的索引来补充自动索引可能很有用。

        通过声明复合主键获得的约束通常比通过创建代理键并声明主键获得的约束更有用。

        同样,以上所有内容都与描述实体之间或实体之间关系的表格有关。描述实体的表应该有一个简单的主键。最好是自然密钥,但在给定数据不提供可靠密钥的情况下,可能需要代理密钥。

        【讨论】:

          【解决方案6】:

          主键是索引与唯一性约束的组合。添加 RelationID 列不会帮助您保持唯一性(因为仍然可以插入相同的 UserID+TargetID 对 - 它们只会获得不同的 RelationID)也不会帮助数据访问(因为如果您将在用户之间加入,则需要 UserID 上的索引和用户关系)。所以第二个似乎是更好的解决方案。

          【讨论】:

            【解决方案7】:

            只是为了搅动锅,您可能需要在这里添加第三列:“关系”。每当我有一个用户:用户关系表时,我都会遇到两个用户有多个关系并且需要将它们分开:

            David Jeff Mentor
            David Jeff Sponsor
            

            在某个时候,指导可能会结束,但您仍然需要赞助商链接。虽然现在可能只有一种关系类型,但将来可能会改变。所以你的主键变成了 UserId、TargetId、RelationshipType。

            【讨论】:

              【解决方案8】:

              我将在这里投入我的两分钱,因为我相信在其他组合的答案中存在一个格式塔,它没有被清楚地呈现。

              1. 由于 Date、Codd 等人为描述概念模型(即实体、属性、关系、键)而设计的术语(即实体、属性、关系、键)已被供应商用来描述该模型的物理实现的属性,因此出现了混淆 (表、列、记录、索引)。在我们讨论这个问题时,理解这两个域是分开的和可识别的至关重要。
              2. 第一范式表明每个关系概念模型中都有一个主键。仅存在于物理模型中的人工密钥不符合条件;但请注意,一些所谓的人工钥匙不在物理模型中,而实际上在概念模型中。 (这方面的一个例子是美国和加拿大的 SSN/SIN,因为没有它,概念模型中的唯一性就无法保证。)下面将详细介绍这一点,但为了避免混淆,我以后将概念模型中的主键称为自然键。 (多个候选者将是候选自然键。)
              3. 当前 RDBMS 实现中的主键约束有多种用途。一种是对物理记录施加唯一性约束,以允许不属于此(物理)主键约束的所有字段就地更新;当自然键的字段可能随时间变化时,因此必须确保没有这样的字段成为物理模型中主键约束的一部分。
              4. 主键约束的另一个用途是启用相关表的外键查找, 最窄的可能主键约束最有效地解决了哪一端。为此,自然键对 主键约束 的选择很糟糕,因为它们通常太宽(人类可理解),无法实现最佳索引大小和高度。
              5. 由此我推断出一个推论:从概念模型到自然键物理模型的最有效转换通常是唯一性约束与独特且不相交的人工主键约束相结合。请注意,在自然密钥需要具有人工组件的情况下(例如 SSN/SIN),需要有一个额外的人工主密钥约束,该约束完全驻留在物理模型中;这是因为有必要允许更改和/或重新分配外部可见的人工编号。 (有多少人知道美国 SSN 在死后几个月会被重新使用?)

              【讨论】:

                【解决方案9】:

                假设您使用 UserRelations 表在用户和目标之间建立多对多关系,那么第一个是不正确的。您希望 UserID+TargetID 是唯一的,否则您最终可能会有多个冗余条目加入同一个 User 和 Target。

                【讨论】:

                  【解决方案10】:

                  关联用户的适当方法 -> 目标是您拥有的第二个选项,因为该方法实际上强制执行查找表的引用完整性。

                  如果没有主键,或者在 UserId 和 TargetId 列中没有唯一性,则可能会有重复的条目,这很可能会导致意外结果。

                  【讨论】:

                    【解决方案11】:

                    特别针对您的问题:在任何情况下,您都需要自然多列上的唯一键。是否将其设为主要取决于您。

                    添加代理键是一个样式问题,并且通常是某些框架所要求的。如果你添加它,在大多数情况下让它成为主要的,只是因为这是框架所期望的。从功能上讲,只要两者都是唯一的,否则就没有区别。

                    【讨论】:

                      猜你喜欢
                      • 2011-04-10
                      • 1970-01-01
                      • 2010-10-18
                      • 2014-06-25
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-02-08
                      相关资源
                      最近更新 更多