从定义上讲,一个关系数据库必须包含规则化的表,并且每一表都包含一个主键。数据库开发人员通常都不赞同使用无意义的数值作为一个表的主键。在技术上而言,这没有对与错之分——这只是一种意见。然而,我们却强烈建议从无意义的数值建立主键,即我们使之作为代理键。在本文中,我们将讨论代理键的强大功能和自然键的固有缺欠。
主键的定义
在讨论之前,我们将定义一下主键(primary key)一词。一个主键是唯一识别一个表的每一记录,但这只是其作用的一部分,主键的主要作用是将记录和存放在其他表中的数据进行关联。在这一点上,主键是不同表中各记录之间的简单指针。所以,主键的值对用户而言是没有什么意义,并且和它要赋予的值也没有什么特别的联系。
当从每一表中选择一个主键时,开发人员必须遵循以下的几条规则:
主键必须唯一的识别每一记录。
一个记录的主键不能为空。
当生成记录时,主键的值必须存在。
主键必须保持稳定——你不能更改主键的域。
主键必须简洁,不要包含过分的属性。
主键的值不能改变。
请注意到,以上列表中的“必须”不是“可能”或者“绝大多数情况下” ——“必须”是绝对的,但这并不意味着你不能打破这一规则。如果你要打破这一规则,只是意味着你的程序不能和关系数据库进行连接。对于关系数据库模型、主键、标准化等方面的更多的信息,请访问以下的网站:
Database Normalization – Definitions(关系数据库标准化——定义)
The University of Texas at Austin
自然键违反了规则
现在让我们看看自然键如何违反以上的规则。为了执行关系数据库规则,一个表不能包含多个记录,这就意味着一个属性必须也只能唯一的识别一个记录。假设一个表存放职工信息,包括职工的名字,出生日期,社会保险号,以及雇用的日期。对于每一个职工都只有一个记录与之对应。
最初的,职工名字是自然主键最合适的候选,但是让我们作深入的研究。在开始建立数据库时,你不能等到知道职工名字才建立一个记录,因为自然主键不能为空。所以,只有知道职工的正确名字后,你才能输入职工的所有信息。
主键允许包含多个域,所以单独的存储姓和名都不是问题。然而,一个公司的职工名字可能有相同的,这一方法即不成立。只要公司里有相同名字,主键就会扩展为至少有三个域,这样你会发觉违反了规则。
纠正错误
现在让我们把以上的例子复杂化。假设你输入的名字有错误。起初,这看起来并不是一个很重要的错误,你只要修改错误的值就可以了。但是请注意,你是不允许修改主键的值。而且这样做也通常会破坏程序的完整性。可以这样,可以更新主键的值,数据引擎即会自动的更新相关的值,但这一方法对于一个新手显然是不合适的。
在我们的范例中,修正一个拼写错误的名字也不会有太多的反应。但如果主键与一个购买顺序号相关联,当主键的输入值有错误时,相关联的购买顺序号的值也会出现混乱,这对程序的完整性有很大的影响。
代理键能遵循规则
显然,使用自然键作为主键会产生很多问题。现在将一个递增的值作为职工表的主键,然后看看结果会是怎么样。最重要的是,递增值必须是唯一的。由于系统能够自动生成这一递增值,可以避免了数据的输入错误。除此之外,当记录有输入时递增值一定会存在,所以主键一定不会为空。
一个代理键是不会改变的,而且只依赖于一个域,所以它特别的简洁。自动递增的域提供了一个唯一的,稳定的,简洁的主键。
其他的争论
很多数据库开发系统把唯一的索引应用到主键,这就消除了多个记录——因为系统不能接受多个记录。然而,你可以人为的使用一个唯一的索引,而不需要将一个主键作为一个索引。人为处理会消耗更多的资源。
一些开发人员认为主键应该识别相关的记录,也就是说,用户应该识别值为“Jane Smith”的主键与职工Jane Smith的记录相关联。如果值为“Jane Smith”的主键没有什么意义,比如只是一个递增的值,这就与职工Jane Smith的信息毫无关系。事实是这样的,没有什么规则确保主键值与记录有什么关联。
在一个设计合理的数据库中,用户是无法看到主键的值。实际上,用户也不需要知道主键是否存在,有时候主键对用户没有什么用处。代理键运行得很好,因为数值与记录之间存在isno的关系。
自然键与代理键的比较
当把这两种主键一一作比较,自然主键将会处于劣势,如表A所示。数据还是数据,数据不会成为系统的指针,因为这些选项不能受开发人员所控制,在程序中或系统自生成的键都是稳定的,它们也不能为空值。
表 A
对于数据库开发人员来说最重要的是在于良好的设计。为了避免出现程序开发之后的错误和过多的修补,我们建议采用代理键。这一简单的方法将为你在程序开发中提供坚固的,稳定的,灵活的基础。