我可以在一个表中有多个主键吗?


Answers:


559

一个表可以具有一个复合主键,该主键是由两列或更多列组成的主键。例如:

CREATE TABLE userdata (
  userid INT,
  userdataid INT,
  info char(200),
  primary key (userid, userdataid)
);

更新: 这是一个链接,其中包含复合主键的更详细描述。


2
在此示例中,需要同时使用userid和userdataid来标识/查找唯一行。不确定OP的意图是什么,但是我来到这里是为了查看我是否可以用一组键之一唯一地标识一行。例如,我想使用用户名或用户ID标识唯一的用户,而无需同时使用两者。我猜想RB对唯一索引的回答就可以解决问题。
墨西哥玉米煎饼

1
@Benitok如RB。的答案中所述,您可以使用唯一索引执行您要查找的内容(一个唯一的索引列,独立于同一表上的其他唯一的索引列)。有关所使用的确切语言语法的详细信息,请务必参考您所喜欢的SQL手册。
4AM

195

您只能有一个主键,但是主键中可以有多个列。

您还可以在表上具有唯一索引,该索引的作用类似于主键,因为它们将强制执行唯一值,并加快对这些值的查询。


39

一个表可以有多个候选键。每个候选键都是唯一的一列或一组列,这些列合在一起,并且也不为空。因此,为任何候选键的所有列指定值就足以确定存在符合条件的一行,或者根本没有任何行。

候选键是关系数据模型中的基本概念。

如果一个表中有多个键,通常的做法是将候选键之一指定为主键。导致表中的任何外键都引用主键而不是其他任何候选键也是一种常见的做法。

我推荐这些做法,但是关系模型中没有任何内容需要在候选键中选择一个主键。


5
同意 在逻辑模型中,所有键都是相等的(没有键是“主键”)。物理实现中哪个密钥获得PRIMARY KEY指定的选择取决于仲裁机构和供应商/产品。
一天的

3
我会说这取决于数据库设计师。
Walter Mitty

我刚刚碰到了一个需要用例的用例。我有一个将由Entity Framework创建/管理的表-据我所知,该表目前不支持非主键唯一的复合约束。但是,它确实支持复合主键。数据也将链接到根本不支持复合键的远程数据库系统。我已经在EF中创建了一个Composite PK,但是还添加了一个不可为null的GUID列,其他系统可以使用该列唯一地进行标识。
克里斯·内维尔

2
克里斯,我说过关系模型不需要主键。对于某些工具是否需要它们,我什么也没说。但我同意你的观点。
沃尔特·米蒂

我认为有一个要求,即最小化PK,即使用最少的列数来唯一标识每个记录。
加里

14

这是主要问题和@Kalmi问题的答案

具有多个自动生成列的意义何在?

下面的代码具有复合主键。其列之一是自动递增的。这仅在MyISAM中有效。InnoDB将生成错误“ 错误1075(42000):错误的表定义;只能有一个自动列,并且必须将其定义为键 ”。

DROP TABLE IF EXISTS `test`.`animals`;
CREATE TABLE  `test`.`animals` (
  `grp` char(30) NOT NULL,
  `id` mediumint(9) NOT NULL AUTO_INCREMENT,
  `name` char(30) NOT NULL,
  PRIMARY KEY (`grp`,`id`)
) ENGINE=MyISAM;

INSERT INTO animals (grp,name) VALUES
    ('mammal','dog'),('mammal','cat'),
    ('bird','penguin'),('fish','lax'),('mammal','whale'),
    ('bird','ostrich');

SELECT * FROM animals ORDER BY grp,id;

Which returns:

+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| fish   |  1 | lax     |
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| mammal |  3 | whale   |
| bird   |  1 | penguin |
| bird   |  2 | ostrich |
+--------+----+---------+

2
如果您首先在主键定义中指定了自动递增列,则此方法有效。(也许情况有所改变,我刚刚在5.6中进行了测试)
CTarczon

11

(已经研究了很多)

候选键 -唯一标识表行所需的最小列组合。
复合键 -2列或更多列。

  • 一个表中可以存在多个候选键
    • 主密钥 - 我们选择的仅候选密钥之一
    • 备用键 -所有其他候选键
      • 主键和备用键都可以是复合键

来源:
https : //en.wikipedia.org/wiki/Superkey
https://en.wikipedia.org/wiki/Candidate_key
https://en.wikipedia.org/wiki/Primary_key
https://en.wikipedia.org / wiki / Compound_key


6

正如其他人所指出的那样,可能有多列主键。但是应注意,如果您有一些键未引入的功能依赖关系,则应考虑规范化关系。

例:

Person(id, name, email, street, zip_code, area)

之间可能存在功能依赖性,id -> name,email, street, zip_code and area 但通常a zip_code与a关联,area因此之间存在内部功能依赖性zip_code -> area

因此,可以考虑将其拆分到另一个表中:

Person(id, name, email, street, zip_code)
Area(zip_code, name)

使其与第三范式一致。


6

主键是非常不幸的表示法,因为“主”的含义以及逻辑模型导致的潜意识关联。因此,我避免使用它。相反,我指的是物理模型的代理密钥和逻辑模型的自然密钥。

重要的是,每个实体的逻辑模型至少应具有一组“业务属性”,其中包括该实体的密钥。Boyce,Codd,Date等在关系模型中将它们称为候选关键字。然后,当我们为这些实体构建表时,其候选键将成为这些表中的自然键。只有通过这些自然键,用户才能唯一地标识表中的行。因为代理密钥应始终对用户隐藏。这是因为代理键没有商业意义。

但是,在没有代理键的情况下,我们表的物理模型在许多情况下效率不高。回想一下,通常只能通过对聚集索引的键查找来找到非聚集索引的未覆盖列(通常暂时忽略实现为堆的表)。当我们可用的自然键变宽时,这(1)扩大了非集群叶节点的宽度,从而增加了存储需求以及对该非集群索引的查找和扫描的读取访问;(2)减少聚集索引的扇出,增加索引高度和索引大小,再次增加聚集索引的读取和存储要求;(3)增加我们的聚集索引的缓存要求。将其他索引和数据移出缓存。

在这里证明了一个小的代理密钥(指定给RDBMS作为“主密钥”)是有益的。当设置为聚簇键时,以便用于从非聚簇索引到聚簇索引的键查找以及从相关表中的外键查找,所有这些缺点消失了。我们的聚簇索引扇出又增加了,以减少聚簇索引的高度和大小,减少了聚簇索引的缓存负载,减少了通过任何机制(无论是索引扫描,索引查找,非聚簇键查找还是外键查找)访问数据时的读取次数并减少我们表的聚集索引和非聚集索引的存储要求。

请注意,仅当代理密钥既小又聚类密钥时,才会产生这些好处。如果将GUID用作群集密钥,则与使用最小可用自然密钥的情况相比,情况通常会更糟。如果表被组织为一个堆,则将使用8字节(堆)的RowID进行键查找,这比16字节的GUID更好,但性能不如4字节的整数。

如果由于业务限制而必须使用GUID,那么寻找更好的群集密钥是值得的。例如,如果一个小的站点标识符和4个字节的“站点序列号”是可行的,那么该设计可能会比GUID作为代理密钥提供更好的性能。

如果堆(可能是哈希联接)的后果使首选存储成为可能,则需要在权衡分析中平衡更广泛的群集密钥的成本。

考虑以下示例:

ALTER TABLE Persons
ADD CONSTRAINT pk_PersonID PRIMARY KEY (P_Id,LastName)

其中元组“ (P_Id,LastName) ”需要唯一性约束,并且可能是一个冗长的Unicode LastName加上4个字节的整数,因此希望(1)声明性地将此约束强制为“ ADD CONSTRAINT pk_PersonID UNIQUE NONCLUSTERED(P_Id ,LastName)和(2)分别声明一个小的代理键作为聚簇索引的“ 主键 ”。值得注意的是,Anita可能只希望将LastName添加到此约束中,以使该字段被覆盖,这在聚簇索引中是不必要的,因为它覆盖了所有字段。

SQL Server中将主键指定为非集群键的能力是一种不幸的历史情况,这是由于将“首选自然键或候选键”(来自逻辑模型)的含义与物理上的“存储中的查找键”的含义相混淆模型。我的理解是,最初SYBASE SQL Server始终使用4字节的RowID(无论是放入堆还是聚集索引中)作为物理模型中的“存储中的查找键”。


3
你能把它翻译成英文吗?
贾西尔,2016年

3

某些人使用“主键”一词来表示一个整数列,该整数列通过某种自动机制生成其值。例如AUTO_INCREMENT在MySQL或IDENTITYMicrosoft SQL Server中。您在这种意义上使用主键吗?

如果是这样,答案取决于您使用的数据库品牌。在MySQL中,您无法执行此操作,但会收到错误消息:

mysql> create table foo (
  id int primary key auto_increment, 
  id2 int auto_increment
);
ERROR 1075 (42000): Incorrect table definition; 
there can be only one auto column and it must be defined as a key

在某些其他品牌的数据库中,您可以在一个表中定义多个自动生成列。


5
具有多个自动生成列的意义何在?
塔尔奈·卡曼(TarnayKálmán),2009年

我没有用例,但是如果有需要,某些品牌的数据库会支持这种情况,而有些则不会。我就是这么说
比尔·卡温

1
这是一种情况:在订单表中,我同时具有一个ID(自动递增)和一个外部ID(类似于哈希的字符串),两者都应该是唯一的,因此从理论上讲,您可以说它们都是“主要的”。当然,这可以通过二级唯一索引来完成,但仍然是合法案例(IMHO)
Nir 18'Aug

2

不能同时拥有两个主键。但是(假设您没有弄乱组合键的情况),可能需要的是使一个属性唯一。

CREATE t1(
c1 int NOT NULL,
c2 int NOT NULL UNIQUE,
...,
PRIMARY KEY (c1)
);

但是请注意,在关系数据库中,“超级键”是属性的子集,可以唯一地标识表中的元组或行。“键”是“超级键”,具有一个额外的属性,可以从键中删除任何属性,从而使该键不再是“超级键”(或者仅仅是“键”就是最小的超级键)。如果还有更多密钥,则所有这些都是候选密钥。我们选择候选键之一作为主键。这就是为什么为一个关系或表谈论多个主键是一种冲突。


维基百科没有“键”的定义。同样,“从键中删除任何属性,使该键不再是'超级键'”对我而言没有任何意义,因为从超级键中删除属性时仍然可以是超级键。
Manohar Reddy Poreddy '18

@ManoharReddyPoreddy是的,在这种情况下,您的属性集不是“键”而是“超级键”。我的意思是,如果一组属性成为“键”,则该组应为最小,或者该组应具有一个附加属性,即从该组中删除任何属性会使所得的组不再是“超级键”。
Rusiru Adithya Samarasinghe '18

看起来,“键”的实际含义是Candidate_key(en.wikipedia.org/wiki/Candidate_key),也许应该这样提。
Manohar Reddy Poreddy '18

@ManoharReddyPoreddy是的,我已经在回答中提到了它。“如果还有更多的密钥,它们都是候选密钥”。无论如何,谢谢您的评论。
Rusiru Adithya Samarasinghe '18

1.当您提到“如果还有更多键,它们都是候选键”,...是不是其他意思,它们都不是候选键?... 2.其他部分在哪里?...我们是同一页吗?
Manohar Reddy Poreddy '18

1

主键是唯一标识记录并在所有索引中使用的键。这就是为什么您不能拥有多个的原因。通常,它也是联接到子表时使用的键,但这不是必需的。PK的真正目的是确保某些东西可以使您唯一地标识一条记录,以便数据更改会影响正确的记录并可以创建索引。

但是,您可以将多个字段放在一个主键(复合PK)中。这将使您的联接变慢(特别是如果它们是较大的字符串类型字段),并且索引较大,但是它可能会消除对某些子表进行联接的需要,因此,从性能和设计上考虑案例依据。执行此操作时,每个字段本身不是唯一的,但它们的组合是唯一的。如果组合键中的一个或多个字段也应该是唯一的,则您需要在其上具有唯一的索引。如果一个字段是唯一的,则很可能是PK的更好候选者。

现在,有时您不止一个PK候选人。在这种情况下,您选择一个作为PK或使用代理密钥(我个人更喜欢此实例的代理密钥)。并且(这很关键!)您向未选择为PK的每个候选密钥添加唯一索引。如果数据需要唯一,则无论是否为PK,都需要唯一索引。这是一个数据完整性问题。(请注意,只要您使用代理键,这也是正确的;人们会因使用代理键而感到麻烦,因为他们忘记了在候选键上创建唯一索引。)

有时您需要多个代理密钥(如果有代理密钥,通常是PK)。在这种情况下,您想要的不是更多的PK,而是更多具有自动生成键的字段。大多数DB不允许这样做,但是有一些解决方法。首先考虑是否可以基于第一个自动生成的键(例如,Field1 * -1)计算第二个字段,或者是否需要第二个自动生成的键确实意味着您应该创建一个相关表。相关表可以一对一关系。您可以通过将父表中的PK添加到子表中,然后将新的自动生成的字段添加到表中,然后再添加适用于该表的任何字段来实施此操作。然后,选择两个键之一作为PK,然后在另一个键上放置一个唯一索引(自动生成的字段不必是PK)。并确保将FK添加到父表中的字段。通常,如果子表没有其他字段,则需要检查为什么认为需要两个自动生成的字段。


0

给出了比我更好的更好的技术答案。我只能添加到此主题:

如果您想要一些不允许的/不可接受的东西,那么最好退后一步。

  1. 了解为什么它不能被接受的核心。
  2. 在文档/期刊文章/网络等中挖掘更多信息。
  3. 分析/审查当前设计并指出主要缺陷。
  4. 在新设计期间考虑并测试每个步骤。
  5. 始终期待并尝试创建自适应解决方案。

希望它能帮助某人。


1
通用(尽管有用)建议,而不是特定问题的答案。
布拉德福德·李约瑟

-3

是的,它在SQL中是可能的,但是我们不能在MsAccess中设置多个主键。然后,我对其他数据库一无所知。

CREATE TABLE CHAPTER (
    BOOK_ISBN VARCHAR(50) NOT NULL,
    IDX INT NOT NULL,
    TITLE VARCHAR(100) NOT NULL,
    NUM_OF_PAGES INT,
    PRIMARY KEY (BOOK_ISBN, IDX)
);

一个SQL表只能有一个PK。
philipxy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.