Answers:
一个表可以具有一个复合主键,该主键是由两列或更多列组成的主键。例如:
CREATE TABLE userdata (
userid INT,
userdataid INT,
info char(200),
primary key (userid, userdataid)
);
更新: 这是一个链接,其中包含复合主键的更详细描述。
一个表可以有多个候选键。每个候选键都是唯一的一列或一组列,这些列合在一起,并且也不为空。因此,为任何候选键的所有列指定值就足以确定存在符合条件的一行,或者根本没有任何行。
候选键是关系数据模型中的基本概念。
如果一个表中有多个键,通常的做法是将候选键之一指定为主键。导致表中的任何外键都引用主键而不是其他任何候选键也是一种常见的做法。
我推荐这些做法,但是关系模型中没有任何内容需要在候选键中选择一个主键。
这是主要问题和@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列或更多列。
来源:
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
正如其他人所指出的那样,可能有多列主键。但是应注意,如果您有一些键未引入的功能依赖关系,则应考虑规范化关系。
例:
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)
使其与第三范式一致。
主键是非常不幸的表示法,因为“主”的含义以及逻辑模型导致的潜意识关联。因此,我避免使用它。相反,我指的是物理模型的代理密钥和逻辑模型的自然密钥。
重要的是,每个实体的逻辑模型至少应具有一组“业务属性”,其中包括该实体的密钥。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(无论是放入堆还是聚集索引中)作为物理模型中的“存储中的查找键”。
某些人使用“主键”一词来表示一个整数列,该整数列通过某种自动机制生成其值。例如AUTO_INCREMENT
在MySQL或IDENTITY
Microsoft 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
在某些其他品牌的数据库中,您可以在一个表中定义多个自动生成列。
不能同时拥有两个主键。但是(假设您没有弄乱组合键的情况),可能需要的是使一个属性唯一。
CREATE t1(
c1 int NOT NULL,
c2 int NOT NULL UNIQUE,
...,
PRIMARY KEY (c1)
);
但是请注意,在关系数据库中,“超级键”是属性的子集,可以唯一地标识表中的元组或行。“键”是“超级键”,具有一个额外的属性,可以从键中删除任何属性,从而使该键不再是“超级键”(或者仅仅是“键”就是最小的超级键)。如果还有更多密钥,则所有这些都是候选密钥。我们选择候选键之一作为主键。这就是为什么为一个关系或表谈论多个主键是一种冲突。
主键是唯一标识记录并在所有索引中使用的键。这就是为什么您不能拥有多个的原因。通常,它也是联接到子表时使用的键,但这不是必需的。PK的真正目的是确保某些东西可以使您唯一地标识一条记录,以便数据更改会影响正确的记录并可以创建索引。
但是,您可以将多个字段放在一个主键(复合PK)中。这将使您的联接变慢(特别是如果它们是较大的字符串类型字段),并且索引较大,但是它可能会消除对某些子表进行联接的需要,因此,从性能和设计上考虑案例依据。执行此操作时,每个字段本身不是唯一的,但它们的组合是唯一的。如果组合键中的一个或多个字段也应该是唯一的,则您需要在其上具有唯一的索引。如果一个字段是唯一的,则很可能是PK的更好候选者。
现在,有时您不止一个PK候选人。在这种情况下,您选择一个作为PK或使用代理密钥(我个人更喜欢此实例的代理密钥)。并且(这很关键!)您向未选择为PK的每个候选密钥添加唯一索引。如果数据需要唯一,则无论是否为PK,都需要唯一索引。这是一个数据完整性问题。(请注意,只要您使用代理键,这也是正确的;人们会因使用代理键而感到麻烦,因为他们忘记了在候选键上创建唯一索引。)
有时您需要多个代理密钥(如果有代理密钥,通常是PK)。在这种情况下,您想要的不是更多的PK,而是更多具有自动生成键的字段。大多数DB不允许这样做,但是有一些解决方法。首先考虑是否可以基于第一个自动生成的键(例如,Field1 * -1)计算第二个字段,或者是否需要第二个自动生成的键确实意味着您应该创建一个相关表。相关表可以一对一关系。您可以通过将父表中的PK添加到子表中,然后将新的自动生成的字段添加到表中,然后再添加适用于该表的任何字段来实施此操作。然后,选择两个键之一作为PK,然后在另一个键上放置一个唯一索引(自动生成的字段不必是PK)。并确保将FK添加到父表中的字段。通常,如果子表没有其他字段,则需要检查为什么认为需要两个自动生成的字段。