规范化理论是用来改造关系模式,通过分解关系模式来消除其中不合适的数据依赖,以解决插入异常、删除异常、更新异常和数据冗余问题。
还是以借书为例,建立一个数据库存储借书信息,包括书号、书名、出版社、学号和借书日期。得到的关系模式的属性集合为:
U = {书号, 书名, 类别号, 类别名, 出版社, 学号, 借书日期}
数据依赖是一个关系内部属性与属性之间的一种约束关系,通过属性间值的相等与否体现出来的数据间相关联系。根据借书信息的现实意义,可以得到:
可以得到属性组 U 上的一组数据依赖 F,如图所示,其中因为书可以被同一位同学在不同时间多次借阅,所以借书时间也是主码。
F = {书号→书名, 书号→类别号, 类别号→类别名, 书号→出版社}
某一时刻该关系模式的实例为:
不难看出,当对这个实例进行增删改查操作时将发生一下问题:
异常情况 | 举例 |
---|---|
数据冗余 | 同一本书的类别号、类别名和出版社重复出现,占用大量空间 |
更新异常 | 当某本书的出版社写错时,需要修改所有这本书的借书记录 |
插入异常 | 如果一本书没有任何同学借过,数据库就无法存储这本书的信息 |
删除异常 | 如果借过同一本书的所有同学都退学了,这本书的信息就会丢失 |
一个好的模式应当不会发生插入异常、删除异常和更新异常,数据冗余应尽可能少。根据关系数据库中的关系满足的不同程度,可以归纳为多种范式,满足最低要求的叫第一范式 1NF,在第一范式中满足进一步要求的为第二范式,其余以此类推。若关系模式 R 满足范式 xNF,记 R∈xNF。
不同级别的范式要求各不相同,因此范式可以作为衡量一个关系模式好坏的标准,将低一级范式的关系模式通过模式分解转换为高一级范式的关系模式集合的过程称为规范化。规范化的基本思想是逐步消除数据以来中不合适的部分,实质上是概念的单一化。
作为一个二维表,关系要符合一个最基本的条件:每一个分量必须是不可分的数据项。即不能以集合、序列等作为属性值, 不能有大表套小表的情况,满足了这个条件的关系模式就属于第一范式(1NF)。
对于结合数据库进行开发的初学者很容易设计出不符合 1NF 的数据库,例如我的 Java 课设开发的 OJ 系统中设计了如图所示的题目集表。
表中的选择题、判断题、编程题和主观题都是以逗号分割的列表形式来存储,这样对于程序设计时是很费劲的。例如对选择题的题号进行增删改操作时,就需要经历以下 5 步,非常啰嗦。
我的博客数据库原理:数据模型和关系数据库有提到过,候选码是它的值能唯一地标识一个元组,而其子集不能标识的属性组。第二范式 2NF的定义是若 R∈1NF,且每一个非主属性完全依赖于 R 的候选码,则 R∈2NF,2NF 在 1NF 的基础上消除了非主属性对码的部分依赖。
例如已知学生关系模式,其中 Sno 学号、Sname 姓名、SD 系名、Sdname 系主任名、Course 课程、Grade 成绩。
S(Sno,Sname,SD,Sdname,Course,Grade)
关系模式S的基本函数依赖如下,不难看出关系模式S的码为 (Sno,Course)。非主属性中的成绩完全依赖于码,而其它非主属性对码的函数依赖为部分函数依赖,所以不属于 2NF,原关系模式 S 属于 1NF。
Sno→Sname, Sno→SD, SD→Sdname, (Sno,Course)→Grade
此时如果对数据进行增删改查操作就会出问题,例如将所有记录都删除时就会丢失系信息。因此消除非主属性对码的函数依赖为部分函数依赖后,将关系模式分解成 2NF 如下:
S1(Sno, Sname, SD, Sdname) S2(Sno, Course, Grade)
第三范式 2NF的定义是若 R∈1NF,且每一个非主属性都不传递依赖于 R 的候选码,则 R∈3NF,3NF 消除了非主属性对码的传递函数依赖。
例如对于上文的关系模式中,关系模式 S1 中存在 Sno→SD,SD→Sdname,即非主属性 Sdname 传递依赖于 Sno,所以 S1 不是 3NF。
S1(Sno, Sname, SD, Sdname) S2(Sno, Course, Grade)
此时如果对数据进行增删改查操作就会出问题,例如将所有同学的信息全部删除,就会丢失所有系信息。进一步分解如下,分解后的关系模式 S11、S12 满足 3NF。
S11(Sno, Sname, SD) S12(SD, Sdname)
对关系模式 S2 不存在非主属性对码的传递依赖,故属于 3NF。所以原模式按如下分解满足 3NF:
S11(Sno, Sname, SD) S12(SD, Sdname) S2 (Sno, Course, Grade)
BCNF 比 3NF 更进了一步,通常认为 BCNF 是修正的第三范式。BCNF 的定义是若 R∈1NF,且每一个属性都不传递依赖于 R 的候选码,则 R∈BCNF。BCNF 范式排除了任何属性对候选键的传递依赖与部分依赖,而 2NF 和 3NF 都是针对主属性的。
例如关系模式 SJP(S,J,P)中,S 是学生,J 表示课程,P 表示名次。每一个学生选修每门课程的成绩有一定的名次,每门课程中每一名次只有一个学生(即没有并列名次)。可得到下面的函数依赖:
F = {(S,J)→P, (J,P)→S}
可知 (S,J) 与 (J,P) 都可以作为候选码,这两个码各由两个属性组成且相交。这个关系模式中显然没有属性对码传递依赖或部分依赖,所以 SJP∈3NF,而且除 (S,J)与(J,P)以外没有其他决定因素,所以 SJP∈BCNF。
看一个不是 BCNF 的例子,例如关系模式 STJ(S,T,J)中,S 表示学生,T 表示教师,J 表示课程。每一教师只教一门课,每门课有若干教师,某一学生选定某门课,就对应一个固定的教师。可得到如下的函数依赖:
F = {(S,J)→T, (S,T)→J, T→J}
可知 (S, J)、(S, T) 都是候选码,因为没有任何非主属性对码传递依赖或部分依赖,所以 STJ 是 3NF。但 STJ 不是 BCNF,因为 T 是决定因素但是 T 不包含码。
如果一个关系数据库中的所有关系模式都属于 BCNF,那么在函数依赖范畴内,已实现了模式的彻底分解,达到了最高的规范化程度,消除了操作异常诸多问题。
如果仅考虑函数依赖这一种数据依赖,BCNF 已经很完美了。但如果考虑其他数据依赖,属于 BCNF 的关系模式 仍存在问题。例如学校中某一门课程由多个教师讲授,相同课程使用相同的一套参考书。可以得到关系模式 Teach (C, T, B),其中课程 C、教师 T 和参考书 B。
该关系模式的码是 (C, T, B) 也就是全码,显然 Teach∈BCNF。但是 Teach 模式中存在如下很多问题,原因是存在多值依赖。
Teach 模式的问题 | 说明 |
---|---|
数据冗余度大 | 有多少名任课教师,参考书就要存储多少次 |
插入操作复杂 | 当某一课程增加一名任课教师时,该课程有多少本参考书,就必须插入多少个元组 |
删除操作复杂 | 某一门课要去掉一本参考书,该课程有多少名教师,就必须删除多少个元组 |
修改操作复杂 | 某一门课要修改一本参考书,该课程有多少名教师,就必须修改多少个元组 |
设 R(U) 是一个属性集 U 上的一个关系模式,X、Y、Z 是 U 的子集且 Z=U-X-Y。多值依赖 X→→Y 的定义是 成立当且仅当对 R 的任一关系 r,r 在 (X,Z) 上的每个值对应一组 Y 的值,这组值仅仅决定于 X 值而与 Z 值无关。例如对于关系模式 Teach(C, T, B),对于 C 的每一个值,T 有一组值与之对应,但是这和 B 取什么值没有关系。
第四范式 4NF 的定义是关系模式 R<U,F>∈1NF,如果对于 R 的每个非平凡多值依赖 X→→Y(Y∉X),X都含有候选码,则 R∈4NF。4NF 在 BCNF 的基础上,消除非平凡且非函数依赖的多值依赖,翻译成人话就是把同一表内的多对多关系删除
例如 Teach(C,T,B) 不属于 4NF,存在非平凡的多值依赖 C→→T,且 C 不是候选码。因此把 Teach 分解为 CT(C, T) 和 CB(C, B) 就满足了 4NF。
《数据库系统概论(第5版)》,王珊 萨师煊 编著,高等教育出版社