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

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

935958 次浏览

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

你也可以在你的表上有唯一索引,这将有点像一个主键,因为它们将强制唯一的值,并将加快这些值的查询。

一个Table可以有一个复合主键,它是一个由两个或多个列组成的主键。例如:

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

更新: 这是一个链接更详细地描述了复合主键。

有些人使用术语“主键”来确切地表示一个整型列,它的值是由某种自动机制生成的。例如,MySQL中的AUTO_INCREMENT或Microsoft SQL Server中的IDENTITY。你是在使用主键吗?

如果是,答案取决于您使用的数据库的品牌。在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

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

一个表可以有多个候选键。每个候选键都是一个列或一组列,它们是UNIQUE的,加在一起也是NOT NULL。因此,为任何候选键的所有列指定值就足以确定是否有一行满足条件,或者根本没有行。

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

通常的做法是,如果一个表中存在多个键,则将其中一个候选键指定为主键。通常的做法是使表的任何外键引用主键,而不是任何其他候选键。

我推荐这些实践,但是关系模型中没有要求在候选键中选择主键。

是的,在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)
);

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

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

下面的代码有一个复合主键。其中一列是自动递增的。这将只在MyISAM工作。InnoDB将生成一个错误"错误的表定义;只能有一个auto列,并且必须定义为键"。

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 |
+--------+----+---------+
正如其他人所指出的,可以有多列主键。 然而,应该注意的是,如果你有一些函数依赖不是由键引入的,你应该将正常化视为你的关系

例子:

Person(id, name, email, street, zip_code, area)
id -> name,email, street, zip_code and area之间可能存在函数依赖关系 但通常zip_codearea相关联,因此zip_code -> area之间存在内部函数依赖

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

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

以便它与第三范式一致。

他们给出了比我更好的技术答案。 我只能补充这个话题:

如果你想要一些不被允许/可接受的东西,这是一个很好的理由后退一步。

  1. 理解为什么这是不可接受的核心原因。
  2. 挖掘更多的文档/期刊文章/网络等。
  3. 分析/审查当前设计并指出主要缺陷。
  4. 在新设计中考虑和测试每一个步骤。
  5. 永远向前看,努力创造适应性的解决方案。

希望它能帮助到一些人。

主键是唯一标识一条记录并在所有索引中使用的键。这就是为什么你只能有一个。它通常也是用于连接子表的键,但这不是必需的。PK的真正目的是确保某些东西允许您惟一地标识一条记录,这样数据更改就会影响正确的记录,从而可以创建索引。

但是,您可以在一个主键中放入多个字段(复合PK)。这将使您的连接更慢(特别是当它们是较大的字符串类型字段时),并且您的索引更大,但它可能消除了在某些子表中进行连接的需要,因此,就性能和设计而言,要视情况而定。当您这样做时,每个字段本身都不是唯一的,但是它们的组合是唯一的。如果复合键中的一个或多个字段也应该是唯一的,那么您需要一个唯一的索引。但是,如果一个字段是唯一的,这可能是一个更好的PK候选人。

有时,您有多个候选PK。在这种情况下,您选择一个作为PK或使用代理键(对于这种情况,我个人更喜欢代理键)。并且(这很关键!)向每个没有被选为PK的候选键添加唯一的索引。如果数据需要是唯一的,那么无论是否是PK,它都需要一个唯一的索引。这是一个数据完整性问题。(注意,任何时候使用代理键都是如此;人们在使用代理键时会遇到麻烦,因为他们忘记在候选键上创建唯一的索引。)

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

主键是一个非常不幸的符号,因为“主键”的内涵和逻辑模型的潜意识关联。因此我避免使用它。相反,我指的是物理模型的代理键和逻辑模型的自然键。

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

然而,如果没有代理键,我们的表的物理模型在很多情况下是低效的。回想一下,非聚集索引的非覆盖列(通常)只能通过Key Lookup查找聚集索引(暂时忽略实现为堆的表)。当我们可用的自然键(s)是宽的,这(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)声明性地强制这个约束为“添加约束pk_PersonID唯一的非集群(P_Id,LastName)”,(2)分别声明一个小代理键为聚集索引的“主键”。值得注意的是,Anita可能只是希望将LastName添加到这个约束中,以便使其成为一个覆盖字段,这在聚集索引中是不必要的,因为它覆盖了所有字段。

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

同时有两个主键是不可能的。但是(假设你没有把复合键搞砸),你可能需要的是让一个属性是唯一的。

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

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

(我一直在研究这些,很多)

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