64

如果我们有这样的表:

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

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

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

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

也是

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

更好,或者:

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

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

4

4 回答 4

56

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

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

  2. 它是不可约的——删除任何列都不会再使其成为键。

  3. 它是一个候选键——一个不可约的超键是一个候选键。

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

我可以推断,Book表键将作为外键出现在少数其他表中,并且也会出现在少数索引中。因此,这将占用相当多的空间——比如三列 x 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 可能是非常好的设计——除非得到不同的证明。

于 2013-01-29T20:04:00.343 回答
24

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

假设您使用 Author 作为主键的一部分创建了 Book 表。

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

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

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

于 2013-01-29T17:18:58.963 回答
7

使用代理主键方案的另一个很好的理由是唯一性约束是否应该在将来发生变化(例如,需要添加 ISBN 以使一本书唯一)。重新键入您的数据会容易得多。

于 2013-01-29T21:30:27.157 回答
4

与此相关的文章有很多。您的情况下复合键的问题:

  1. 很难将书籍与其他实体联系起来
  2. 很难在网格中编辑它们,因为大多数网格不支持复合键(例如 kendo 网格、jqgrid)
  3. 您可能拼错了作者、标题、版本

规范化您的数据并像 (dasblinkenlight) 建议的那样仅向作者存储一个 ID 也是很好的。最坏的情况是,他/她会改变他/她的名字(例如她结婚了,她喜欢她的新名字)。

于 2013-01-29T17:29:19.513 回答