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

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

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

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

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

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

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

一个表可以有多个候选键。 每个候选键都是一列或一组独立的列,并且也是非空的。 因此,为任何候选键的所有列指定值足以确定有一行符合标准,或者根本没有行。

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

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

我推荐这些做法,但是关系模型中没有什么需要在候选键中select一个主键。

这就是主要问题和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 | +--------+----+---------+ 

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

每个实体的逻辑模型至less有一组“业务属性”,这些“业务属性”包含了实体的一个密钥,这一点很重要。 Boyce,Codd,Date等人在关系模型中将这些引用为候选键。 当我们为这些实体构build表时,候选键将成为这些表中的自然键。 只有通过这些自然键,用户才能够唯一地标识表中的行; 因为代理键应该总是从用户隐藏。 这是因为代理键没有商业意义。

然而,在没有代理键的情况下,我们的表的物理模型在许多情况下将是低效的。 回想一下,非聚集索引的非覆盖列只能通过对聚集索引进行密钥查找(通常情况下)来忽略表格。 当我们可用的自然关键字很宽时,(1)扩大了非集群叶节点的宽度,增加了存储需求,并读取了对非聚集索引的search和扫描访问; (2)减less我们的聚集索引增加索引高度和索引大小扇出,再次增加我们聚簇索引的读取和存储需求; (3)增加我们的聚集索引的caching需求。 从caching中追踪其他索引和数据。

这是一个小的代理键,被指定为RDBMS的“主键”certificate是有益的。 当被设置为集群密钥时,以便用于从非聚簇索引到相关表的外键查找的聚集索引中的键查找,所有这些缺点都消失了。 我们的聚簇索引扇出再次增加,以减less聚集索引的高度和大小,减less聚簇索引的caching负载,通过任何机制访问数据时减less读取(索引扫描,索引查找,非聚簇键查找或外键查找)并减less我们表的聚簇索引和非聚簇索引的存储需求。

请注意,这些好处只发生在代理键很小和集群密钥的情况下。 如果使用GUID作为聚类密钥,情况通常会比使用最小的可用自然密钥更糟糕。 如果表被组织成堆,那么8字节(堆)的RowID将被用于密钥查找,这比16字节的GUID好,但是性能比4字节的整数要好。

如果由于业务限制而必须使用GUID,那么search更好的集群密钥是值得的。 如果例如一个小的站点标识符和4字节的“站点序列号”是可行的,那么该devise可能比作为代理密钥的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添加到这个约束中,以便创build一个覆盖的字段,这在聚集索引中是不必要的,因为所有字段都被覆盖。

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

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

例:

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

id -> name,email, street, zip_code and area之间可以有一个函数依赖关系。但是通常一个zip_code与一个area相关联,因此在zip_code -> area之间有一个内部函数依赖关系。

因此可以考虑把它分成另一个表格:

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

这与第三范式是一致的。

有些人使用术语“主键”来表示完全是一个整数列,通过一些自动机制来获取它的值。 例如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 

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

主键是唯一标识logging的键,用于所有索引。 这就是为什么你不能有一个以上。 这通常也是用于join子表的关键,但这不是必需的。 一个PK的真正目的是确保东西允许你唯一地标识一条logging,以便数据改变影响正确的logging,并且可以创build索引。

但是,您可以将多个字段放在一个主键(一个组合PK)中。 这会使你的连接速度变慢(特别是如果它们是更大的stringtypes的字段)并且你的索引更大,但是它可能不需要在一些子表中进行连接,所以就性能和devise而言,以案件为依据 当你这样做时,每个领域本身并不是唯一的,但它们的组合是。 如果组合键中的一个或多个字段也应该是唯一的,那么你需要一个唯一的索引。 如果一个领域是独一无二的,这很可能是PK的一个更好的select。

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

偶尔有时候你需要多个代理键(如果你有它们,通常是PK)。 在这种情况下,你想要的不是更多的PK,它是更多的字段与自动生成的键。 大多数数据库不允许这样做,但有办法绕过它。 首先考虑是否可以基于第一个自动生成的键(例如Field1 * -1)来计算第二个字段,或者可能需要第二个自动生成的键真的意味着您应该创build相关的表。 相关的表格可以是一对一的关系。 您可以通过将父表中的PK添加到子表中,然后将新的自动生成的字段添加到表中,然后为该表添加适合的字段。 然后select两个键中的一个作为PK,并在另一个上放置一个唯一索引(自动生成的字段不一定是PK)。 并确保将FK添加到父表中的字段。 一般来说,如果你没有额外的子表的字段,你需要检查你为什么认为你需要两个自动生成的字段。

好的技术答案比我能做得更好。 我只能添加到这个主题:

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

  1. 理解为什么它是不可接受的核心。
  2. 在文档/期刊文章/网页等中挖掘更多内容
  3. 分析/审查当前的devise和重点缺陷。
  4. 在新devise中考虑并testing每一步。
  5. 总是期待并尝试创造适应性解决scheme。

希望它会帮助别人。

有两个主键在同一时间,是不可能的。 但是(假设你还没有弄明白这个问题),你可能需要的是使一个属性唯一。

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

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

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