Mark Seemann这篇博客文章反对使用自然键作为数据库表中的主键,而是建议始终使用合成(人工)键。
什么是自然键
自然键(也称为业务键或领域键 )是数据库中一种唯一键,由存在并在数据库外部世界(即业务领域或论域)中使用的属性组成。[3]在关系数据模型中,自然键是超键,因此是关系中所有属性的 功能决定因素。
自然键有两个互补的用途:
- 它为数据提供了唯一的标识方法
- 它施加一条规则,特别是唯一性约束,以确保数据在信息系统中保持唯一性
自然键不同于代理键,后者在数据库之外没有任何意义,不基于现实世界的观察,也不旨在作为对所建模现实的陈述。因此,自然键提供了一定的数据质量保证,而代理键则没有。数据元素通常有多个键,其中任意数量的键可以是自然键或代理键。
原文观点:
我目前正在学习数据库和信息系统的本科课程。由于这门课程面向没有实际经验的学生,因此它明智地按照教学进度进行。为了教授数据库键,它从自然键开始。
从教学角度来看,这是有道理的,到目前为止,与我一起工作的年轻人现在提出使用自然键进行数据库设计。
我没有责怪任何人。你必须先学会爬,然后才能走路。
然而,这种情况让我思考以下问题:自然键是否是一个好主意?
让我们考虑一个例子。对于我们正在做的一个小项目,我们创建了一个世界 50 家最佳餐厅的数据库。我的同学建议采用这样的表格设计:
CREATE TABLE Restaurants (
year TEXT NOT NULL,
rank TEXT NOT NULL,
restaurantName TEXT NOT NULL,
cityName TEXT NOT NULL
);
当然,在这一点上,这个表格定义根本没有定义任何键。我对此并无怨言。毕竟,一个月前,学生们可能还没见过数据库表。
不过,根据课程设置,为 Restaurants 表定义一个由 restaurantName、cityName 和 year 组成的键是很自然的。假设名称和城市可以唯一标识一家餐馆。
在这个特殊的例子中,这个假设可能真的成立。到此为止。毕竟,数据集并不算大,而且对于该州中的餐厅来说,拥有可识别的名称非常重要。如果让我猜,世界上可能只有一家 Nobelhart & Schmutzig。
不过,一个好的软件架构师还是应该对基本假设提出质疑。
名字和城市是天然的关键吗?
很容易想象这不是。
如果我们把关键字扩展到国家这个字段呢?
好吧,但如果我们在美国斯普林菲尔德开了一家名为 "China Wok "的餐馆呢?
很难说是独一无二的。
如果再加上州呢?
可能还是不唯一的。
标识
确保唯一性只是自然键众多问题中的第一个。你可能很快就会得出结论,对于餐厅数据库来说,合成键可能是最好的选择。
那么 "自然 "的自然键呢?
- 汽车底盘编号就是一个例子。这已经是一个不透明的数字,而且很可能来自某个数据库。
- 那么个人身份号码呢?在丹麦,我们有 CPR 号码,据我所知,美国的社会安全号码也大致类似。
如果你正在设计的数据库已经包含了这样一个个人身份号码,你可能会倾向于使用它作为自然键。毕竟,它在其他地方已经是一个键了,所以可以保证是唯一的,不是吗?
- 是的,这个身份号码可以唯一地识别一个人,但反过来就不一定了。
- 一个人可能有不止一个识别码。至少当时间是一个因素时是这样。
例如,由于技术和历史原因,丹麦的 CPR 号码带有个人出生日期和性别等信息(按键不应该带有这些信息)。自 2014 年起,一项新的法律允许变性公民获得一个新的 CPR 号码,以反映他们所认为的性别。这样做的后果是,同一个人可能拥有不止一个 CPR 号码。也许不会同时拥有一个以上,但一生中肯定会拥有两个。
即使现有的键保证是唯一的,你也不能假定唯一性会导致双射。如果使用外部唯一键,就可能无法跟踪要跟踪的实体。
这不仅适用于人,也适用于汽车、自行车(也有底盘编号)、网卡等。
数据录入错误
最后,即使你已经找到了一个自然键,它可以保证是唯一的,并且可以跟踪你想要跟踪的实际实体,但还有一个反对在系统中使用外部定义键的理由:
- 数据录入错误。
就拿我的汽车底盘编号来说吧:
虽然我住在哥本哈根,大部分时间都是步行或骑自行车在城里转悠,但我还是拥有一辆老爷车,以便在国内其他地方代步。在丹麦,汽车每隔一年就要接受一次强制性的官方检查,我一生中也经历过几次这样的检查。几年前,负责检查的机械师告诉我,我的汽车底盘编号有误。
这确实让我有点紧张,因为我买的是二手车,我突然担心事情并不像我想的那样。难道我无意中买了一辆赃车?
但机械师只是走到他的电脑前更正了错误。这时,一种不同的不安袭上心头。当你从事编程工作几十年后,你就会学会预见各种典型的故障模式。由于底盘编号是自然钥匙的一个明显候选项,我已经预料到更改这个编号要么被证明是不可能的,要么会产生各种连锁反应,最终导致官方记录不再承认这辆车是我的。
但事实证明,不管是谁制作了那款软件,他们都知道自己在做什么,因为机械师只是更改了底盘编号,仅此而已。现在这已经是五六年前的事了,我仍然拥有同一辆车,官方的所有权记录也从未出现过任何问题。
发现汽车底盘编号错误的机械师将其明确解释为笔误(数据输入错误)。
经过几十年的编程生涯,我认识到数据迟早会出错。
- 要么是笔误,
- 要么是最终用户输入错误,
- 要么是从外部系统导入时出现数据转换错误。
- 甚至是同一系统内的数据转换错误,因为它要经历升级和迁移。
关键点:
- 系统的设计应允许对数据进行更正。这包括外部键的更正,如底盘编号、政府 ID 等。
- 这就意味着您不能在自己的系统中使用这些键作为数据库键。
结论:
无论您认为自然键有多稳定,它都可能发生变化,从而导致更新异常和完整性问题。
自然键对于数据库设计来说从来都不是一个好主意,而应该始终使用数据库生成的合成(人工)键作为主键。