学习数据库范式理论后,尽管已经知道范式能够帮助优化数据库设计,但是在使用中却发现,很难将这些理论方便的应用到实际中。本节主要梳理下如何在实际应用中使用范式。
范式有很多种,参考关系数据库设计可知有第一范式、第二范式、第三方式、BC范式、第四范式、第五范式、域-码范式等。
当关系模式满足第一范式时,该关系模式所有属性的域都是原子的。常见的非原子域实例如下:
简单来说,第一范式要求属性的域都是原子的(不可再分)。对于多值属性,为多值集合中的每一项创建一条元组。所以无需考虑。注意,这里只讨论是否满足第一范式,对于属性中包含非原子值的场景,这里不进行扩展讨论。
第一范式只是关系模式众多范式中最基础的一个模式,其本身依旧存在许多致命的问题。如数据冗余,插入、删除、更新等异常。假设关系模式R为:
(学生ID,姓名,课程号,课程名,所在系,系主任,成绩)
上述关系模式属于第一模式。然而这样的关系模式什么问题呢?比如从数据冗余角度来说,我们有理工大类学生A,选了数学与英语课。那么在关系模式中则会存有两个字段:
(A_ID1,A,课程号1,数学,理工大类,AAA,1.0)
(A_ID2, A,课程号2,英语,理工大类,AAA,1.0)
从这里可以看出,除了课程的相关属性,其他属性完全一样,这明显是数据冗余。
如果来一个转学生B,转学生B如果没有选课,但是学生信息必须先录入,那该怎么办呢?录入数据如下:
(B_ID,B,NULL,NULL,理工大类,AAA,NULL)
当我们在录入课程号和课程名时,完全无法填写,只能插入空值,如果添加了对课程号和课程名的非空校验,那么将无法插入新记录,从而造成插入异常。
若调整了某门课程的学分,数据表中所有行的”学分”值都要更新,否则会出现同一门课程学分不同的情况。
假设一批学生已经完成课程的选修,这些选修记录就应该从数据库表中删除。但是,与此同时,课程名称和学分信息也被删除了。
第二范式(Second Normal Form, 2NF)只具有历史的意义,并没有在实际中使用。这里着重介绍BC范式。
Boyce-Codd范式可以消除所有基于函数依赖能够发现的冗余。具有函数依赖集F的关系模式R属于BCNF的条件是,对 F + F^+ F+中所有形如α → \rightarrow →β的函数依赖(α ⊆ \subseteq ⊆R且β ⊆ \subseteq ⊆R),下面至少有一项是成立:
也就是说,对于满足BC范式的关系模式,这个关系模式中的所有函数依赖,要么该依赖是平凡的函数依赖(存在包含关系),要么函数依赖的左侧部分是超码。所以判断函数是否遵循BCNF,可以按照如下步骤判断:
if 该函数依赖是平凡的依赖
该函数依赖遵循BCNF
return true
else if 该函数依赖的左侧部分是超码
该函数依赖遵循BCNF
return true
else
该函数依赖不遵循BCNF
return false
只要关系模式中存在一个函数依赖不遵循BCNF,那么该关系模式就不满足BC范式。
如果需要使用仓库存储物品,且每个仓库只有一个管理员,且定义仓库管理关系表为StoreHouseManage(仓库ID, 存储物品ID, 管理员ID, 数量),那么这个数据库表有如下函数依赖:
由于管理员ID和仓库ID的一对一映射关系,所以(仓库ID, 存储物品ID)和(管理员ID, 存储物品ID)都可作为StoreHouseManage的候选码。因为函数依赖(仓库ID) → \rightarrow →(管理员ID) 和 (管理员ID) → \rightarrow →(仓库ID)的左侧部分均不是超码,所以不满足BCNF范式。
对于不属于BCNF的范式,需要对其进行分解。设R为不属于BCNF的一个模式,则存在至少一个非平凡的函数依赖α → \rightarrow →β,其中α不是R的超码。那么在设计中可以使用以下两个模式取代R:
当分解不属于BCNF的模式时,产生的模式可能有一个或多个不属于BCNF。在这种情况下,需要进一步分解,其最终结果是一个BCNF模式集合。更多分解实现参考关系数据库设计的分解算法。
对于上述示例,将仓库管理关系表分解为二个关系表:
一次分解后,可以验证,上述两个关系模式都是满足BCNF的。
BCNF使得函数依赖的实施变得困难(有可能破坏关系模式上的函数依赖)。对于希望保持函数依赖,可以考虑另外一种比BCNF弱的范式——第三范式。
第三范式是比BCNF弱的范式。任何满足BCNF的模式也满足3NF。具有函数依赖集F的关系模式R属于第三范式(third normal form)的条件是:对 F + F^+ F+中所有形如α → \rightarrow →β的函数依赖(α ⊆ \subseteq ⊆R且β ⊆ \subseteq ⊆R),下面至少有一项是成立:
前两个条件与BCNF定义中的两个条件相同。第三个条件,在某种意义上,代表BCNF条件的最小放宽,以确保每一个模式都有保持依赖的3NF分解。对于判断一个函数依赖是否遵循第三范式,可以按照如下步骤判断:
if 该函数依赖是平凡的依赖
该函数依赖遵循3NF
return true
else if 该函数依赖的左侧部分是超码
该函数依赖遵循3NF
return true
else if 该函数依赖的右侧部分去除与左侧部分重复的属性后剩余的属性均在候选码中
该函数依赖遵循3NF
return true
else
该函数依赖不遵循3NF
return false
BCNF可以消除所有基于函数依赖所能发现的冗余(注意,BCNF不能确保冗余被消除),但无法保证设计是保持依赖的。
3NF可以在满足无损的同时并保持依赖。但3NF也有一个缺点:可能不得不用空值表示数据项间的某些可能有意义的联系,并且存在信息重复的问题。
对使用函数依赖进行数据库设计的目标是:
由于不是总能达到所有三个目标,因为我们需要在BCNF和保持依赖的3NF中做出选择。
在不能得到保持依赖的BCNF分解的情况下,通常我们倾向于选择BCNF,因为在SQL中检查非主码约束的函数依赖很困难。
函数依赖可以消除函数依赖发现的冗余,对于无法由函数依赖发现的冗余,则不能保证一定被消除。如多值依赖产生的冗余。
函数依赖和多值依赖集为D的关系模式r®属于第四范式(4NF)的条件是,对 D + D^+ D+中所有形如α → \rightarrow → → \rightarrow →β的多值依赖(其中α ⊆ \subseteq ⊆R且β ⊆ \subseteq ⊆R),至少有以下之一成立:
可以看到,4NF定义与BCNF定义的唯一不同在于多值依赖的使用。与BCNF类似,4NF可以消除所有基于多值依赖能够发现的冗余,同时无法保证设计是保持依赖的。相比函数依赖,多值依赖的保持问题更加复杂,这里不扩展讨论。更多该问题的讨论可参考《数据库系统概念》一书的附录C.1.2。
判断一个多值依赖是否遵循第四范式,可以按照如下步骤判断:
if 该多值依赖是平凡的多值依赖
该函数依赖遵循4NF
return true
else if 该多值依赖的左侧部分是超码
该函数依赖遵循4NF
return true
else
该多值依赖不遵循4NF
return false
四范式不是“最终”的范式。多值依赖有助于理解并消除某些形式的信息重复,而这种信息重复用函数依赖是无法理解的。
还有一些类型的约束称作连接依赖(join dependency),它概化了多值依赖,并引出了另一种范式称作投影-连接范式(Project-Join Normal Form, PJNF)(PJNF在某些书中称为第五范式,fifth normal form)。PJNF不仅难以推导,而且没有形成一套具有正确有效性和完备性的推理规则用于约束的推导。因此PJNF很少使用。
还有一类更一般化的约束,它引出一种称作域-码范式(Domain-Key Normal Form, DKNF)。同PJNF一样,DKNF不仅难以推导,而且没有形成一套具有正确有效性和完备性的推理规则用于约束的推导。因此DKNF也很少使用。
第二范式(Second Normal Form, 2NF)只具有历史的意义(没有实用意义),所以没有对它进行深入讨论。关系模式R属于第二范式,如果R中的每个属性A都满足如下准则之一:
函数依赖α → \rightarrow →β称为部分依赖(partial dependency)的条件是,存在α的真子集γ使得γ → \rightarrow →β。我们称β部分依赖于α。
范式并没有终点,更多的认识问题,会有更多的范式出现。
一般情况下,数据库设计至少保证第三范式,对于是否保证BC范式或第四范式,还需根据具体情况判断。所以在范式的实际应用中,第一范式必须满足, 第三范式、BC范式、第四范式要根据实际需求进行权衡。这需要对实践的不断积累才能取舍。后续会不断补充这一部分。
数据库系统概念(第六版) A. Silberschatz H. F. Korth S. Sudarshan著 杨冬青 等译 第八章
https://zhuanlan.zhihu.com/p/22337031 如何理解关系模式三范式?
https://www.cnblogs.com/awidy/articles/3978724.html 三大范式和BC范式