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

时间:2008-10-20 09:56:23

标签: database database-design primary-key composite-primary-key

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

12 个答案:

答案 0 :(得分:506)

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

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

更新: Here is a link,其中包含有关复合主键的更详细说明。

答案 1 :(得分:179)

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

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

答案 2 :(得分:34)

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

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

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

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

答案 3 :(得分:13)

这是主要问题和@ 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 :(得分:7)

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

每个实体的逻辑模型必须至少具有一组"业务属性"其中包含实体的密钥。 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)

元组&#34; (P_Id,LastName)&#34;需要唯一性约束,并且可能是冗长的Unicode LastName加上4字节整数,(1)声明性地强制执行此约束为&#34; ADD CONSTRAINT pk_PersonID UNIQUE NONCLUSTERED(P_Id,LastName)< / EM>&#34; (2)单独宣布一个小的代理密钥为&#34; 主密钥&#34;聚集索引。值得注意的是,Anita可能只希望将LastName添加到此约束中以使其成为覆盖字段,这在聚簇索引中是不必要的,因为所有字段都被它覆盖。

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

答案 5 :(得分:6)

如其他人所述,可以使用多列主键。 但是应该注意的是,如果你有一些functional dependencies没有被密钥引入,你应该考虑normalizing你的关系。

示例:

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)

这样与third normal form一致。

答案 6 :(得分:5)

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

只能有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

答案 7 :(得分:2)

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

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

答案 8 :(得分:2)

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

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

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

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

答案 9 :(得分:2)

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

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

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

答案 10 :(得分:1)

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

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

  1. 了解为什么它不可接受的核心。
  2. 在文档/期刊文章/网络等方面进行更多挖掘。
  3. 分析/检讨当前设计并指出主要缺陷。
  4. 在新设计中考虑并测试每一步。
  5. 始终期待并尝试创建自适应解决方案。
  6. 希望它会帮助某人。

答案 11 :(得分:-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)
);