复合主键与附加“ID”列?

时间:2013-01-29 17:09:38

标签: sql sql-server database database-design

如果我们有这样的表:

书籍(假装“ISBN”不存在)

  • 作者
  • 标题
  • 出版年份
  • 价格

有人可能会说{Author,Title,Edition}可能是候选/主键。

什么决定候选/主键应该是{Author,Title,Edition}还是应该使用ID列,{Author,Title,Edition}是唯一的索引/键约束?

所以

  • 作者(PK)
  • 标题(PK)
  • 版(PK)
  • 出版年份
  • 价格

更好,或者:

  • ID(PK)
  • 作者
  • 标题
  • 出版年份
  • 价格

其中{Author,Title,Edition}是另一个唯一索引/约束?

4 个答案:

答案 0 :(得分:37)

假设{Author,Title,Edition}唯一标识一本书,则以下内容成立:

  1. 它是一个(超级)键 - 唯一标识一个元组(行)。

  2. 这是不可简化的 - 删除任何列都不会使它成为关键。

  3. 这是候选键 - 不可缩减键是候选键。

  4. 现在让我们考虑ID(整数)

    我可以推断Book表键在少数其他表中显示为外键以及少数索引。因此,它将占用相当多的空间 - 比如三列×40个字符(或者其他......) - 在每个表中加上匹配的索引。

    为了使这些“其他”表和索引更小,我可以在Book表中添加一个唯一整数列,以用作将作为外键引用的键。说出类似的话:

    alter table `Book` add `BookID` integer not null identity;
    

    BookID也是(必须)是唯一的,Book表现在有两个候选键。

    现在我可以选择BookID作为主键。

    alter table Book add constraint pk_Book primary key (BookID);
    

    但是,{Author,Title,Edition} 必须保留一个密钥(唯一)才能阻止这样的内容:

    BookID  Author      Title           Edition
    ----------------------------------------------- 
      1      C.J.Date  Database Design     1
      2      C.J.Date  Database Design     1
    

    总结一下,添加BookID - 并选择它作为主要内容 - 并没有阻止{Author,Title,Edition}成为(候选)密钥。它仍然必须有自己唯一的约束,通常是匹配的索引。

    另请注意,从设计角度来看,此决定是在“物理层面”完成的。 通常,在设计的逻辑层面上,此ID不存在 - 它是在考虑列大小和索引时引入的。因此物理模式源于逻辑模式。根据数据库大小,RDBMS和使用的硬件,这些大小推理都不会产生可测量的影响 - 因此使用{Author,Title,Edition}作为PK可能是完美的设计 - 直到证明不同。

答案 1 :(得分:14)

通常,您不希望主键更改值。这就是使用盲或代理主键的原因。

假设您创建了Book表,其中Author是主键的一部分。

假设你在大约一年后发现你拼错了“Ray Bradbury”。或者更糟糕的是,你拼错了“Rachael Bloom”。想象一下,您需要修改多少个数据库行来纠正拼写错误。想象一下,必须更改多少个索引引用。

但是,如果您有一个带有代理键的Author表,则只需更正一行。不需要更改索引。

最后,数据库表名通常是单数(Book),而不是复数(Books)。

答案 2 :(得分:6)

使用代理主键方案的另一个好理由是,未来是否应该更改唯一性约束(例如,需要添加ISBN以使书籍唯一)。重新输入数据会更容易。

答案 3 :(得分:3)

有很多与此相关的文章。 您案例中复合键的问题:

  1. 很难将图书与其他实体联系起来
  2. 很难在网格中编辑它们,因为大多数网格都不支持复合键(例如kendo grid,jqgrid)
  3. 您可能会拼错作者,标题,版本
  4. 标准化您的数据并将一个ID存储到作者(如#dasblinkenlight)建议也是一件好事。在最糟糕的情况下,他/她将改变他/她的名字(例如,她已经结婚了,她喜欢她的新名字)。