为什么要使用多列作为主键(复合主键)

125

这个例子是取自w3schools

CREATE TABLE Persons
(
    P_Id int NOT NULL,
    LastName varchar(255) NOT NULL,
    FirstName varchar(255),
    Address varchar(255),
    City varchar(255),
    CONSTRAINT pk_PersonID PRIMARY KEY (P_Id,LastName)
)

我理解的是,P_IdLastName这两列一起代表了表Persons的主键。这个理解正确吗?

  • 为什么有些人想要使用多列作为主键,而不是单列?
  • 在一个给定的表中,可以使用多少列作为主键?

现在第二个问题也有答案了。 - Wolf
1
@Martijn Peters。为什么答案被删除了? - PerformanceDBA
9个回答

132

你的理解是正确的。

在许多情况下,您会这样做。一个例子是像 OrderHeaderOrderDetail 这样的关系。在 OrderHeader 中的主键可能是 OrderNumber。在 OrderDetail 中的主键可能是 OrderNumberLineNumber。如果只使用其中任何一个,它将不是唯一的,但两个组合在一起则保证唯一。

另一种方法是使用生成的(非智能)主键,例如在此情况下使用 OrderDetailId。但这样做,您就无法轻松地看到关系。有些人更喜欢一种方式,有些人更喜欢另一种方式。


2
如果我正在使用branch_id并在两个数据库之间使用复制,那么这是否有用,可以解决ID的重复问题?! - Mhmd
14
请注意,在许多使用生成的主键的情况下,您通常仍希望在组合值上拥有唯一键。 - Bacon Bits
请详细说明“有些人喜欢一种方式,有些人喜欢另一种方式”的含义。 - Username
2
请详细说明?我不确定该说什么。我认识一些人喜欢将多个连接字段作为键,因为直观地更容易理解他们正在查看的内容。我也认识其他人喜欢为每行分配一个唯一的键,因为这样更容易和更快速地输入。这是你想问的吗? - MJB
那条消息是发给@用户名的,我忘记指定了。 - MJB

28

另一个复合主键的例子是使用关联表。假设你有一个包含一组人员的人员表和一个包含一组群组的群组表。现在你想在人员和群组之间创建多对多的关系,也就是说每个人可以属于多个群组。以下是使用复合主键的表结构。

Create Table Person(
PersonID int Not Null,
FirstName varchar(50),
LastName varchar(50),
Constraint PK_Person PRIMARY KEY (PersonID))

Create Table Group (
GroupId int Not Null,
GroupName varchar(50),
Constraint PK_Group PRIMARY KEY (GroupId))

Create Table GroupMember (
GroupId int Not Null,
PersonId int Not Null,
CONSTRAINT FK_GroupMember_Group FOREIGN KEY (GroupId) References Group(GroupId),
CONSTRAINT FK_GroupMember_Person FOREIGN KEY (PersonId) References Person(PersonId),
CONSTRAINT PK_GroupMember PRIMARY KEY (GroupId, PersonID))

很好的解释:我认为在规范化的情况下,m-to-n关系需要属性是关键。 - Wolf
1
以下是有关编程的内容,请将其翻译成中文。请仅返回已翻译的文本:可以添加一些好处的解释会更好。 - MartianMartian

9

W3Schools的示例没有说明何时应使用复合主键,仅提供使用与其他键相同的示例表的示例语法。

他们选择的示例可能会通过组合一个无意义的键(P_Id)和一个自然键(LastName)来误导您。这种奇怪的主键选择表示以下行根据模式是有效的,并且是唯一标识学生所必需的。直觉上讲,这是没有意义的。

1234     Jobs
1234     Gates

进一步阅读: 关于主键的争论 或搜索 meaningless primary keys 或查看这个 SO问题 FWIW - 我建议避免使用多列主键,而是使用单个生成的ID字段(替代键)作为主键,并在必要时添加其他(唯一)约束条件。

1
  1. “great primary key debate”链接特别愚蠢,信息自我服务和虚假。 2)在使行唯一的列上创建索引是必要的。带有索引的“代理”ID始终是额外的列和额外的索引。这非常荒谬,因为它是冗余且更慢的。
- PerformanceDBA
3
“主键争论”并不是愚蠢的。对于那些不是SQL开发人员或SQL DBA,并且不会一直在SQL中工作的开发人员来说,这是一个非常有效的问题。即使在纯SQL中,我也宁愿使用无意义的自动生成的键作为主键进行连接,而不是要记住传递n位数据作为自然键。您可以持有自己的观点,但我们希望您不要如此轻率地对待这个问题。 - Robert Paulson

4

当您想要确保几个属性的组合的唯一性时,可以使用复合键(具有多个属性的键)。单个属性键无法实现相同的效果。


1
关于确保唯一键,您可以依赖于两个属性的组合来形成逻辑上不可能重复的键。例如,从较大的数据集中选择“人员”和“毕业日期”作为唯一键。 - John Mark

2
是的,它们都构成了主键。特别是在没有替代键的表中,可能需要指定多个属性作为每个记录的唯一标识符(不好的例子:一个既有名字又有姓氏的表可能需要将它们的组合作为唯一标识符)。

2

一般情况下,一个键中存在多个列会比使用替代键表现得更差。我倾向于使用替代键并在多列键上创建唯一索引。这样可以获得更好的性能且保持唯一性。更重要的是,当该键中的某个值发生更改时,您不需要在215个子表中更新一百万个子条目。


1
  1. 性能。不适用于 SQL 平台(也许适用于虚拟的“sql”和免费软件)。
  2. 偏好无关紧要。表格所需的完整性是相关的。
  3. “替代” ID 与索引始终是额外的列和额外的索引。因此,在任何平台上都会更慢。就性能而言,您自相矛盾。
  4. 如果您不知道如何正确地更新神话般的“215个子表中的百万个子条目”,请提问。
- PerformanceDBA
2
我不同意“在一般情况下,关键字中的多个列通常比代理键表现得更差”的说法。通常,在考虑关系时需要额外的查询来获取代理键。此时,从性能角度来看,需要进行完整的额外往返,因此速度会变慢。 - ttugates

2

你的第二个问题

在给定表中可以一起用作主键的列数是实现特定的:它定义在实际使用的DBMS中。[1],[2],[3] 您必须检查您使用的数据库系统的技术规范。有些非常详细,而有些则不是。寻找有关此类限制的网络信息可能很困难,因为术语会有所不同。术语复合主键应该是必需的 ;)

如果找不到明确的信息,请尝试创建测试数据库以确保您可以预期稳定(且具体)地处理限制违规情况(这是可以预期的)。小心获取有关此事的正确信息:有时将累积限制,并且您将看到不同的结果具体取决于不同的数据库布局。



1
在关系型数据库中,如果你使用一个中间表,那么在多个表上使用主键会很方便。我将以一个我曾经制作的数据库为例,具体涉及其中三个表。几年前,我为一个网络漫画创建了一个数据库。其中一个表叫做“comics”,列出了所有漫画、它们的标题、图像文件名等信息。主键是“comicnum”。
第二个表是“characters”,包含他们的姓名和简短描述。主键在“charname”上。
由于每个漫画(有一些例外)都有多个角色,并且每个角色都出现在多个漫画中,在“characters”或“comics”中放置一列来反映这一点是不切实际的。相反,我创建了第三个表,“comicchars”,它列出了哪些角色出现在哪些漫画中。由于这个表本质上连接了两个表,所以它只需要两列:charname和comicnum,并且主键在两者上。

0

我们创建复合主键来保证构成单个记录的列值的唯一性。它是一个约束条件,有助于防止插入不应重复的数据。

例如:如果所有学生ID和出生证件号都唯一分配给单个人,则将人的主键设置为学生ID和出生证明号的组合将是一个好主意,因为它可以防止您意外插入拥有不同学生ID但相同出生证明的两个人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接