425

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

4

12 回答 12

620

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

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

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

于 2008-10-20T10:00:10.060 回答
217

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

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

于 2008-10-20T09:57:40.817 回答
44

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

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

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

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

于 2008-10-21T21:00:53.753 回答
16

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

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

下面的代码有一个复合主键。它的一列是自动递增的。这仅适用于 MyISAM。InnoDB 会产生一个错误“ ERROR 1075 (42000): Incorrect table definition; can only be one auto column and it must be defined as a key ”。

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 |
+--------+----+---------+
于 2011-06-17T08:16:55.323 回答
14

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

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

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

来源:
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/复合键

于 2018-04-08T04:57:44.913 回答
6

正如其他人所指出的,可以有多列主键。然而应该注意的是,如果你有一些没有被键引入的函数依赖,你应该考虑规范化你的关系。

例子:

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

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

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

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

使其符合第三范式

于 2011-06-17T08:27:11.970 回答
6

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

重要的是,每个实体的逻辑模型都至少有一组“业务属性”,这些“业务属性”构成了实体的键。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,无论是在堆还是聚集索引中,作为物理模型中的“存储中的查找键”。

于 2016-04-04T05:57:34.900 回答
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

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

于 2008-10-20T17:53:50.107 回答
3

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

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

现在有时,您有不止一个 PK 候选人。在这种情况下,您选择一个作为 PK 或使用代理键(我个人更喜欢这种情况下的代理键)。并且(这很关键!)您为每个未被选为 PK 的候选键添加唯一索引。如果数据需要唯一,不管是不是PK,都需要一个唯一的索引。这是一个数据完整性问题。(请注意,任何时候使用代理键时也是如此;人们会因为代理键而陷入麻烦,因为他们忘记在候选键上创建唯一索引。)

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

于 2013-05-28T15:42:57.417 回答
3

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

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

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

于 2017-09-29T21:53:47.247 回答
1

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

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

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

希望它会帮助某人。

于 2012-09-16T19:09:51.510 回答
-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)
);
于 2009-01-07T11:44:55.743 回答