多列主键?

时间:2008-12-08 21:21:00

标签: database-design

例如,我有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作为主键。

我的问题是每种设计的含义是什么,哪种性能更好?

11 个答案:

答案 0 :(得分:9)

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

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

答案 1 :(得分:7)

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

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

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

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

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

答案 2 :(得分:3)

据我所知(实际上,实际上它很有意义),主键识别一些独特的数据......至少在标准化表中。如果表中的数据需要通过密钥更明确地标识,则应使用复合主键(其中包含多个列的主键)。

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

AppointmentDate,CustomerID,AppointmentReason

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

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

对于您的情况,有助于获得有关将包含哪种数据的更多信息,但是我可以将UserID和TargetID作为复合主键以及TargetID是UserRelations表的外键(如果它出现)在您的用户表中。我这样做是因为如果你有一个名为RelationID的主键,你最终会得到一个重复的用户列,这可能会对性能产生负面影响,并且根本不会使你的表正常化。

答案 3 :(得分:2)

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

当这些性能影响变得显着时(甚至可能在他们这样做之前),您应该切换到使用无意义的“代理”密钥(通常是整数),用于所有其他目的(FK,联接,指数,查询搜索谓词,应用程序代码实体Identifers等。)

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

答案 4 :(得分:2)

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

您获得的约束要求每个参与列都是非空的,并且需要参与列的唯一性。

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

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

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

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

答案 5 :(得分:1)

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

答案 6 :(得分:1)

为了搅拌锅,你可能需要第三列:“关系”。任何时候我都有一个用户:用户关系表,我遇到了两个拥有多个关系并需要将它们分开的用户:

David Jeff Mentor
David Jeff Sponsor

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

答案 7 :(得分:1)

我要在这里抛出我的两分钱,因为我相信在其他答案的组合中有一个格式塔没有清楚地呈现。

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

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

答案 10 :(得分:0)

特别针对您的问题:在任何情况下,您都需要在自然多列上使用唯一键。是否让它成为主要由你决定。

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