何时不应使用代理主键?

12

我有几个数据库表,只包含了一列和很少的行,通常只是另一个系统中定义的某个ID。这些表然后被其他表使用外键引用。例如,一个表包含国家代码(SE,DK,US等)。所有值始终是唯一的自然键,并且它们在其他(遗留)系统中被用作主键。

似乎没有必要为这些表引入新的代理键,对吗?

一般来说,在什么情况下不应使用代理键?

6个回答

27
我认为必须满足以下几个条件:
  • 你的自然键必须是绝对、肯定、不允许例外的唯一的(像姓名、社会安全号码等通常看起来是唯一的,但实际上并不是)

  • 你的自然键应该尽可能小,例如不超过4个字节的INT类型大小(不要在主键中使用VARCHAR(50),特别是在SQL Server中不要在聚集键中使用VARCHAR(50)!)

  • 你的自然键应该是稳定的,即永远不会改变(好吧,对于ISO国家代码来说,这几乎是一个假设——除非像南斯拉夫或苏联这样的国家崩溃,或者像两个德国那样合并——但这种情况很少见)

如果满足这些条件,你可以考虑将自然键作为主键——但这应该是所有表中的2%的例外情况,而不是常规情况。


1
国家的分裂和合并本身并不是关键变化的好例子;当一个实体被分成许多部分或许多实体被合并时,无论这些实体如何被识别,都会带来相当大的问题。尽管如此,对于自然键的标准非常好(即非常严格),还是要点个赞。个人认为2%是一个高估值,哈哈。 - Yarik

4
我不确定是否有例外情况,不能使用代理键。我认为代理键的本质是使引用在全局范围内唯一,这在应用于您描述的系统时特别相关。
虽然您提到的每个卫星主键在其自己的范围内可能是唯一的,但您无法保证它们将在整个互连环境中保持唯一,特别是如果它扩展。我怀疑最初的设计者是在试图未雨绸缪他们的系统或追随他们学到的最新潮流 ;)

2
自然键也是全局唯一的,否则它就不是自然键了。如果你的意思是代理键在整个数据库中是唯一的,那么你是在说你使用一个计数器在所有表之间共享吗?! - andrew cooke
如果自然键全局唯一是真的,那么根本不需要代理键。自然键往往在其选择范围内是唯一的,但通常不会超出这个范围。在像企业这样的环境中工作时,可能会有许多类似的系统随着时间的推移(并通过公司购买)由公司的各种元素创建,您经常会发现自然键要么不唯一,要么更糟糕的是,一个目标有多个键。 - Lazarus
我应该补充说明,我非常注重数据仓库的方式。为了在系统之间交换数据,我倾向于使用中间网关并承受性能损失,以便将来可以扩展。 - Lazarus

2
这是一个长期存在的争论。如果你在谷歌上搜索“代理键与自然键”(surrogate v natural keys),你会得到很多链接。因此,我怀疑你在这里得到的将是一场辩论,而不是一个清晰的答案。
来自这篇文章的引用:
数据建模者(在本次讨论中,我包括任何为数据库设计表的人)对这个问题有不同的看法:有些建模者坚信使用代理键;而另一些人则宁愿使用自然键也不会改变。关于数据建模和数据库设计的文献支持两种观点,除了在数据仓库领域,在那里代理键是维度和事实表的唯一选择。

“……其他人宁愿死也不会使用除自然键以外的任何东西……”说得好。也许他们中的许多人已经去世或辞职了。 :) 这可能解释了代理键阵营的普遍存在。 - Yarik

1
自然键(在您的情况下是国家代码)更好,因为:
  • 当您看到它们时,它们有意义(仅使用代理键对用户没有任何意义。这对于通常需要使用原始数据库输出的DB开发人员和维护人员非常重要)
  • 较少的连接(通常只需要国家代码,它们已经在其他表中。如果使用代理键,则需要加入查找表)
自然键的缺点是它们与信息逻辑相关联,如果它发生变化(有时会发生),则需要修改许多表格,基本上彻底改造数据库的重要部分。
因此,如果在您的数据库中逻辑长期不变,请使用自然键。

1
肯定不应该向用户展示代理键,它们被用于关联数据。通常我会向用户展示主键(除非它们是自然键,在这种情况下它们被呈现为“键”,而不是数据本身)。 - Lazarus
我在我的回复中补充道,这对开发来说是一个优势。通常你会有一个表格的文本转储,或者在另一个非IDE环境中工作,无法查找参考表。在这种情况下,使用替代键会显著减慢工作速度。 - culebrón
这是真的,除非代理是记录的一部分(并且适当地索引),否则随着对数据需求的增长,链接表或其他使用的方法的开销可能会变得显著。 - Lazarus
看着我的第一条评论,我已经完全搞砸了英语。应该是“我通常不会提供主键”和“不作为'keys'呈现”。有时候我把一个句子搞砸的能力让我很惊讶。 - Lazarus

0
除了marc_s所说的,通常在一个链接表中,你不需要一个代理键,这是一个只包含两个不同主键的表,用于创建多对多关系。一般来说,在这里两个字段上的复合键就可以很好地工作了。这是我建议使用复合键的少数情况之一,一般情况下我更喜欢使用代理键和复合键上的唯一索引。

0

如果自然键能够被真正信任,那么将其用于标识目的是一个好主意。请参阅Marc_S的回答,了解一些自然键不能被信任的情况。不要过于担心效率问题。即使像VIN(车辆识别号码)这样长的东西也不会拖慢你的数据库。如果你认为会有影响,可以进行一些测试,注意效率并非线性扩展。

声明主键的主要原因是防止表滑出第一范式,从而不再表示关系。使用自动递增的代理键可能会导致两个具有不同id字段但否则相同的行。这将带来一些不符合第一范式数据的问题。而且用户无法提供帮助,因为他们看不到id字段。

如果一张表的行可以通过两个或多个外键的组合来确定,那么你拥有一个关系表,有时称为链接表或联接表。通常最好声明由所有所需外键组成的复合主键。

如果上述选择导致性能缓慢,有时可以通过创建一些额外的索引来解决问题。这取决于你对数据的操作方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接