既然都叫最简单的数据库“范式”教程,我觉得它一定要满足这个要求:看完这篇博客,你一定会明白数据库的“范式”和那些诸如“完全函数依赖”,“部分函数依赖”,“传递函数依赖”等烦人的概念,前提是你得跟着我的思路认真的读完它,好,准备好你的半个小时了吗?我们开始。
范式其实就是关系数据库规范程度的级别
,举个我们生活中的例子,老师让打扫卫生,最低标准是扫地
,二级标准是扫地+擦桌子
,三级标准是扫地+擦桌子+擦玻璃
,实际上在标准不断上升的过程中,首先高一级的标准是满足低一级标准的,范式也是如此,只不过它的标准描述的是数据库规范化的程度罢了。数据库中的范式有1NF , 2NF , 3NF , BCNF , 4NF,5NF(本文不讨论)。它们之间的关系1NF < 2NF < 3NF < BCNF < 4NF
(默认越大 级别越高)。
这个例子有下面的关系
(学号,课名)->分数
学号->姓名
学号->系名
系名->系主任
注:A->B:理解为A属性可以唯一确定B属性。
这个例子会在说第二范式和第三范式的时候被逐步优化。
数据库表的每一列都是不可再分的原子数据项,它是一个关系型数据库的最低标准,如果不满足第一范式,那么这个数据库就不是关系型数据库。如下所示:
这个表我们无法在数据库中直接创建,因为它的系
这一列,既有系名
又有系主任
,产生了冲突,如果作出调整,使它符合第一范式,那么应该是这样的:
但是即使这样,数据库中还是会存在诸如插入、删除、数据冗余等异常。比如针对下面的表:
因此这个数据库的设计是有缺陷的,为了优化它,我们继续到第二范式。在说第二范式之前,我们先来说几个重要的概念。
函数依赖:在一张表中,如果给定A属性(或属性组)的值,一定能唯一确定B属性的值,那么就说B依赖于A属性(或属性组),记作A->B,比如给定学号,就能确定姓名,一个学号一定能确定一个姓名,就这么简单,是不是似曾相识?对,我们在第二部分中说那个贯穿全文的例子的时候其实已经用到这个概念了。
完全函数依赖:在函数依赖的基础上,我们的A属性是一个属性组,只有这个属性组中的全部属性才能确定唯一的B属性,A的任何一个子集都不可以。比如(学号,课名)->成绩,而单独的学号或者课名都不能确定成绩,这就叫完全函数依赖。
部分函数依赖:和完全函数依赖相比,A属性组中的部分属性就能确定B属性,其它属性可有可无,比如(学号,课名)->姓名,其实只要学号就可以了,这样的依赖就叫部分函数依赖。
传递函数依赖:如果A->B,B->C,并且B不能->A(防止直接A->C),那么我们可以得出结论A->C,叫做C传递函数依赖A。比如学号->系名,系名->系主任,并且(系名不能决定学号),所以系主任传递函数依赖学号。
码:一个属性或者属性组,使得整个关系中除过此属性或者属性组之外的其余属性都
完全函数依赖
于它,那它就是码。例如(学号,成绩),它们两个的组合可以将其他所有的属性都决定了,(学号,课名)->分数,学号->姓名,学号->系名,学号->系名->系主任,所以(学号,课名)就是这个关系中的一个码,那这个关系中还有别的码吗? 谁知道呢,但我们的分析过程应该是这样的:从一个属性一直到n个属性全部来一遍。
- 一个属性:学号,姓名,系名,系主任,课名,分数。找反例即可:学号不能决定分数,(必须要课名),姓名什么也决定不了,系名只能决定系主任,课名也决定不了什么,分数一样。所以一个属性中肯定是没有码的。
- 两个属性:(学号,姓名),(学号,系名),(学号,系主任),(学号,课名),(学号,分数),(姓名,系名),(姓名,系主任),(姓名,课名),(姓名,分数),(系名,系主任),(系名,课名),(系名,分数),(系主任,课名),(系主任,分数),(课名,分数)。呼~终于完了,我们再进行分析,发现只有(学号,课名),这个组合与其他的关系来说是完全函数依赖。也就是它们是一个码。
- 三个属性:……
- 四个属性:……
- 五个属性:……
- 六个属性:……
好吧,只能这样吗?对,只能这样,其实有一些能减少工作量的方法,比如如果我们在分析两个属性时已经确定(学号,课名)是
码
,那么在以后的分析中,如果包含(学号,课名)的那就一定不是码,因为要完全函数依赖。比如(学号,课名,系名)就不用分析了。其他的方法大家自己总结吧。
- 主属性:所有的码中包含的属性都是主属性。
- 非主属性:除过码中包含属性之外的属性。
呼呼~,返过头再看一遍,我们要向第二范式出发了。
在第一范式基础上,消除非主属性对主属性的部分函数依赖,针对我们的例子,分析如下:
主属性:学号,课名
非主属性:姓名,系名,系主任,分数
目前只有一张表:
(学号,姓名,系名,系主任,课名,分数)
明显,姓名和系名都是对(学号,课名)存在部分函数依赖的。因为其实只需要学号就可以决定姓名和系名。所以这个表目前是不符合第二范式的,我们只好对表作出分解,当然分解的模式不是唯一的,下面只是一种情况,如下所示:
选课(学号,课名,分数)
学生(学号,姓名,系名,系主任)
我们此时再来看两张表是否满足第二范式:
选课表:主属性:(学号,课名)。非主属性:分数。 不存在非主属性对主属性的部分函数依赖,满足第二范式。
学生表:主属性:学号。非主属性:系名,系主任,姓名。因为主属性只有一个,所以一定不存在非主属性对主属性的部分函数依赖。满足第二范式。
至于怎么找主属性,不用我强调了吧,要是忘了的话回头看看上面。此时数据变成了这样:
我们回头看看前面的问题有没有得到改善:
因此,只满足第二范式也还是不够。还是存在好多问题。
在第二范式基础上,消除非主属性对主属性的传递函数依赖,同样,我们继续分析我们的例子:
主属性:学号,课名
非主属性:姓名,系名,系主任,分数
目前我们的表如下:
选课(学号,课名,分数)
学生(学号,姓名,系名,系主任)
我们发现在学生表中:存在非主属性系主任
对主属性学号
的传递函数依赖。因为学号->系名,系名->系主任
,所以才会出现前面那么多的问题。那我们试着再次分解学生表,消除这种传递函数依赖。分解后如下:
选课(学号,课名,分数)
学生(学号,姓名,系名)
系(系名,系主任)
我们继续分析:
选课表中码为(学号,课名),非主属性为分数,它们是完全函数依赖的关系,不存在非主属性对主属性的传递函数依赖。符合第三范式。
对于学生表,码为学号,主属性为学号,非主属性为系名,不可能存在非主属性对于码的传递函数依赖,符合第三范式,
对于系表,码为系名,主属性为系名,非主属性为系主任,不可能存在非主属性对于码的传递函数依赖(至少要有三个属性才可能存在传递函数依赖关系),也符合第三范式。
此时数据成了现在这样:
我们再回头看看前面的问题有没有得到改善:
当数据库到达第三范式的时候,基本上有关数据冗余,数据插入、删除、更新的异常问题得到了解决,这也是一个”合法的”数据库最基本的要求,但是效率问题就另当别论了,因为表越多,连接操作就越多,但是连接是一个比较耗资源的操作。对于我们前面的例子,已经优化到最好了,也没有再次优化的地方。下面我们说到BC范式的时候会说另一个例子。
在第三范式基础上,消除主属性对主属性的部分函数依赖与传递函数依赖,什么?没搞错吧?对,没搞错。我们看下面的例子。
若:
1:某公司有若干个仓库;
2:每个仓库只能有一名管理员,一名管理员只能在一个仓库中工作;
3:一个仓库中可以存放多种物品,一种物品也可以存放在不同的仓库中。每种物品在每个仓库中都有对应的数量。
如下所示:
属性有:仓库,管理员,物品,数量。
我们先来找码:
我们得到主属性:管理员,物品,仓库
非主属性:数量
不存在数量对主属性的部分函数依赖和传递函数依赖,有的人可能有疑问?明明(仓库,物品)->数量,为什么不是部分函数依赖,是因为我们这里说的主属性是码,此时的三个主属性是由两个码(管理员,物品),(仓库,物品)合起来组成的,它们之间任何一个和数量都不能部分函数依赖。因此这个表是满子第三范式的,但是它还是存在下面的问题:
既然都满足第三范式了,那为什么还是会出现这么多的问题呢?因为存在主属性对主属性的部分函数依赖,此例子中存在(管理员,物品名)->仓库
,但是实际上管理员->仓库
。因此就存在了主属性对主属性(码)的部分函数依赖。
我们将其模式分解:
仓库(仓库名,管理员)
库存(仓库名,物品名,数量)
再次回头看与原来的问题有没有得到解决
符合BCNF的数据库已经非常严格了。但是这种严格只是在函数依赖
层面的。啥?上升层面?不是吧,对!接下来要说的第四范式研究的领域是多值依赖
层面的。
那我们还是开门见山的来看一个例子:
Teaching(C,T,B),C表示课程,T表示老师,B表示参考书。
有下面未规范化的关系:
我们先让它满足1NF如下所示:
发现存在许多问题:
我们再来看它到底属于第几范式。发现这个关系中C、T、B都是唯一的,也就是全键。不存在非主属性,也不存在主属性对主属性的的部分函数依赖和传递函数依赖(因为那样码中至少要两个主属性),它只有一个码(C、T、B)。也就是这个关系是属于BCNF的。那它又为什么会出现这么多问题呢?就是因为它存在多值依赖。
多值依赖:在关系模式R(U)中,X、Y、Z是U的子集,Z=U-X-Y。如果多值依赖X->->Z成立,那么给定(x,y),能确定z的一组值,而且这个值仅由x决定,与y没有关系。
比如:在Teaching中,C、B、T是Teaching的子集,T=Teaching-C-B。给定(C,B),比如(数据库原理与应用,SQL Server 2000),能确定一组T值(邓宇,孙泽)。但是这组T值只与C(数据库原理与应用)有关,与B(SQL Server 2000)没有关系,我们就说C->->T。叫做T多值依赖于C。
平凡多值依赖:如果y为空集。X->->Z就是平凡多值依赖
非平凡多值依赖:y不为空。
其实已经分析完了,就是因为Teaching关系中存在多值依赖,所以才会由我们说的问题,我们进行模式分解,分解得到的表如下所示:
TC表(课程,教师)
BC表(课程,参考书)
看看我们的问题有没有得到解决:
现在的表是满足第四范式的,说了这么多,我们最后给出第四范式的定义:关系模式R满足1NF的前提下,如果R中每一个非平凡多值依赖X->->Y,X中都含有键,则R属于4NF,前面讨论的关系中。键是(T、C、B),是全键。C->->B,C->->T,这种多值依赖是非平凡的,但是单独的C不是键,所以就无法满足第四范式。
如果认真的看到这里,我想你一定有所收获,那么赞一个吧。
参考资料:刘慰老师