首页 文章

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

提问于
浏览
326

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

12 回答

  • 173

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

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

  • 6

    表可以包含 Composite Primary Key ,它是由两列或更多列组成的主键 . 例如:

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

    Update: Here is a link,其中包含复合主键的更详细说明 .

  • 7

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

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

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

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

  • 33

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

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

    重要的是,每个实体的逻辑模型至少具有一组“业务属性”,其包括实体的密钥 . Boyce,Codd,Date等在关系模型中将这些称为候选键 . 然后,当我们为这些实体构建表时,它们的候选键在这些表中成为自然键 . 只有通过那些自然键,用户才能唯一地识别表中的行;因为代理键应始终对用户隐藏 . 这是因为代理键没有商业意义 .

    然而,在没有代理键的情况下,我们的表的物理模型在许多情况下效率低下 . 回想一下,非聚集索引的非覆盖列只能通过密钥查找(通常)找到聚簇索引(忽略作为堆实现的表) . 当我们的可用自然密钥很宽时,这(1)扩大了我们的非聚簇叶节点的宽度,增加了存储要求,并且对非聚集索引的搜索和扫描进行了读取访问; (2)减少聚集索引的扇出,增加索引高度和索引大小,再次增加聚簇索引的读取和存储要求; (3)增加了我们的聚簇索引的缓存要求 . 从缓存中追逐其他索引和数据 .

    这是一个小的代理键,被指定为RDBMS作为“主键”证明是有益的 . 当设置为聚类键时,为了用于从非聚簇索引和相关表中的外键查找进行聚簇索引的键查找,所有这些缺点都消失了 . 我们的聚簇索引扇出再次增加以减少聚簇索引的高度和大小,减少聚簇索引的缓存负载,减少通过任何机制访问数据时的读取(无论是索引扫描,索引搜索,非聚簇键查找还是外键查找)并降低表的聚簇索引和非聚簇索引的存储要求 .

    请注意,仅当代理键很小且聚类键时才会发生这些好处 . 如果使用GUID作为聚类密钥,则情况通常比使用最小可用自然密钥时更糟 . 如果表被组织为堆,那么8字节(堆)RowID将用于键查找,这比16字节GUID更好但性能低于4字节整数 .

    如果由于业务限制必须使用GUID,那么搜索更好的群集密钥是值得的 . 例如,如果一个小的站点标识符和4字节的“站点序列号”是可行的,那么该设计可能比作为代理键的GUID具有更好的性能 .

    如果堆的结果(可能是散列连接)使得该首选存储成为更广泛的集群密钥的成本,则需要将其 balancer 到权衡分析中 .

    考虑这个例子::

    ALTER TABLE Persons
    ADD CONSTRAINT pk_PersonID PRIMARY KEY (P_Id,LastName)
    

    元组“(P_Id,LastName)" requires a uniqueness constraint, and may be a lengthy Unicode LastName plus a 4-byte integer, it would be desirable to (1) declaratively enforce this constraint as " ADDCONSTRAINT pk_PersonID UNIQUE NONCLUSTERED(P_Id,LastName)" and (2) separately declare a small Surrogate Key to be the "聚簇索引的主键 . 值得注意的是,Anita可能只希望将LastName添加到此约束中以使其成为覆盖字段,这在聚簇索引中是不必要的因为所有字段都被它覆盖 .

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

  • 2

    如其他人所述,可以具有多列主键 . 但应该注意的是,如果你有一些不是由密钥引入的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一致 .

  • 477

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

    只能有1个主键 .
    但是你可以有多个备用键 .

    简单来说就是这种情况:

    • 表中可以有多个 Candidate keys (最小列,用于唯一标识行) .

    • 其中一个候选键是专门选择的,称为 Primary Key

    • 所有其他候选键称为 Alternate keys

    • 主键和备用键都可以是 Compound key 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

  • 13

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

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

  • -2

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

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

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

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

  • 1

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

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

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

  • 2

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

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

    • 了解为什么不可接受的核心 .

    • 在文档/期刊文章/网络等方面挖掘更多内容 .

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

    • 在新设计期间考虑并测试每一步 .

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

    希望它会帮助某人 .

  • 4

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

相关问题