提问



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

最佳参考


表可以包含复合主键,它是由两列或更多列组成的主键。例如:


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


更新:这是一个链接,其中包含复合主键的更详细说明。[15]

其它参考1


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


您还可以在表上使用唯一索引,这将有点像主键,因为它们将强制执行唯一值,并将加快查询这些值。

其它参考2


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


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


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


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

其它参考3


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



  拥有多个自动生成列有什么意义?



下面的代码具有复合主键。其中一列是自动递增的。这只适用于MyISAM。 InnoDB将生成错误 ERROR 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 |
+--------+----+---------+

其它参考4


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


重要的是,每个实体的逻辑模型至少具有一组业务属性,其包括实体的密钥。 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)分别声明一个小的Surrogate Key为聚簇索引的 Primary Key 。值得注意的是,Anita可能只希望将LastName添加到此约束中以使其成为覆盖字段,这在聚簇索引中是不必要的,因为所有字段都被它覆盖。


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

其它参考5


如其他人所述,可以具有多列主键。
但应该注意的是,如果你有一些不是由密钥引入的功能依赖,你应该考虑规范你的关系。[16] [17]


例:


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)


这样它与第三范式一致。[18]

其它参考6


有些人使用术语主键来表示一个整数列,它通过某种自动机制生成其值。例如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


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

其它参考7


主键是唯一标识记录的密钥,用于所有索引。这就是为什么你不能拥有多个。它通常也是用于连接子表的密钥,但这不是必需的.PK的真正目的是确保某些东西可以让你唯一地识别记录,以便数据更改影响正确的记录,以便可以创建索引。


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


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


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

其它参考8


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


只能有1个主键。

但是你可以有多个备用键。


简单来说就是这种情况:



  • 可以多个候选键(最小列,以唯一标识行)。

  • 其中一个候选键是专门选择,称为主键

  • 所有其他候选键名为备用键

  • 两个主键和备用键可以是复合键 s(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 [19] [20] [21] [22]

其它参考9


以比我能做的更好的方式给出了良好的技术答案。
我只能添加到这个主题:


如果你想要一些不允许/不可接受的东西,那么就有理由退一步。



  1. 了解为何不接受的核心。

  2. 在文档/期刊文章/网络等方面进行更多挖掘。

  3. 分析/审查当前设计并指出主要缺陷。

  4. 在新设计中考虑并测试每一步。

  5. 始终期待并尝试创建自适应解决方案。



希望它会帮助某人。

其它参考10


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


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


但请注意,在关系数据库中,超级密钥是唯一标识表中元组或行的属性的子集。 密钥是一个超级密钥,它具有从密钥中删除任何属性的附加属性,使该密钥不再是超级密钥(或简称为密钥是最小超级密钥)。如果有更多键,则所有键都是候选键。我们选择一个候选键作为主键。这就是为什么谈论一个关系或表的多个主键是一个冲突。

其它参考11


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