跳转到内容

闭卷专题二:规范化理论

规范化理论对应教材第6章“关系数据理论”,也是闭卷考核内容。它研究的问题可以概括为:

一个关系表怎样设计,才能减少数据冗余、避免修改异常,并且保持原有业务约束?

这部分常见题型包括:

根据函数依赖求候选码;
判断关系模式属于第几范式;
指出插入、删除、更新异常;
把关系模式分解到3NF或BCNF;
判断分解是否无损连接、是否保持函数依赖。

假设将学生、课程和选课成绩全部放在一张表中:

Enroll(Sno, Sname, Dept, Cno, Cname, Teacher, Grade)

一位学生选多门课程时,姓名和院系会重复保存;一门课程被多名学生选择时,课程名和教师也会重复保存。

样例:

SnoSnameDeptCnoCnameTeacherGrade
S01张明计算机C01数据库王老师88
S01张明计算机C02操作系统李老师92
S02李欣软件C01数据库王老师85

这种设计可能出现以下问题。

学生姓名、院系随着每次选课反复保存;
课程名称、教师随着每个学生选课反复保存。

如果课程 C01 的教师更换,需要修改多行。漏改任何一行,就会出现同一课程对应两个教师的矛盾数据。

如果新开设一门课程但还没有学生选课,在这张大表中可能无法单独保存课程信息,因为主键中需要学生信息。

如果删除最后一个选修 C01 的学生记录,课程 C01 的名称和教师信息也会随之丢失。

规范化的目标,就是通过合理分解关系模式来减少这些异常。


在关系模式 R 中,如果属性组 X 的值确定后,属性组 Y 的值也被唯一确定,就称:

Y 函数依赖于 X

记作:

X → Y

例如:

Sno → Sname, Dept
Cno → Cname, Teacher
(Sno, Cno) → Grade

含义分别是:

学号能够确定学生姓名和院系;
课程号能够确定课程名称和教师;
一个学生对一门课程的选课组合能够确定成绩。

函数依赖不能只看当前数据碰巧没有重复,而要根据业务规则判断。

例如当前表中没有重名学生,也不能因此写:

Sname → Sno

因为现实规则并没有保证不同学生绝不重名。


候选码是能够唯一确定关系中全部属性,并且本身不可再删减的属性组。

对于:

Enroll(Sno, Sname, Dept, Cno, Cname, Teacher, Grade)

若函数依赖为:

Sno → Sname, Dept
Cno → Cname, Teacher
(Sno, Cno) → Grade

则:

(Sno, Cno) → 所有属性

而单独 Sno 或单独 Cno 都不能确定成绩和全部信息。因此候选码为:

(Sno, Cno)

从候选码中选一个作为主要标识,称为主码。若只有一个候选码,通常它就是主码。

主属性:出现在任一候选码中的属性。
非主属性:不出现在任何候选码中的属性。

在上例中:

主属性:Sno、Cno
非主属性:Sname、Dept、Cname、Teacher、Grade

4. 完全依赖、部分依赖与传递依赖

Section titled “4. 完全依赖、部分依赖与传递依赖”

如果 Y 依赖于组合属性 X,而删除 X 中任何一个属性后都不能确定 Y,则称 Y 完全函数依赖于 X

例如:

(Sno, Cno) → Grade

单靠学生号不能确定某门课程成绩,单靠课程号也不能确定某个学生成绩。因此成绩完全依赖于联合码。

如果 Y 依赖于联合码,但实际上仅依赖联合码的一部分,则称为部分依赖。

例如在 Enroll 中:

(Sno, Cno) → Sname

但实际上:

Sno → Sname

因此 Sname 对候选码 (Sno, Cno) 是部分依赖。同理:

Dept 部分依赖于 Sno;
Cname、Teacher 部分依赖于 Cno。

如果:

X → Y
Y → Z
且Y不是候选码,Z不直接依赖于X的码含义

则可以说 Z 传递依赖于 X

例如关系:

Student(Sno, DeptNo, DeptName)

函数依赖:

Sno → DeptNo
DeptNo → DeptName

则:

Sno → DeptName

属于传递依赖。


一个关系模式中的每个属性值都必须是不可再分的原子值,才满足第一范式。

不符合1NF的例子:

StudentIDStudentNamePhones
S01张明138xxxx, 139xxxx

Phones 一列中保存多个电话号码,不便于查询和约束。

改进方法:

Student(StudentID, StudentName)
StudentPhone(StudentID, Phone)
1NF解决的是属性值不可再分的问题。
关系数据库中的规范表通常首先必须满足1NF。

关系模式满足1NF,并且每一个非主属性都完全函数依赖于任一候选码,则满足2NF。

当候选码是联合码时,尤其要检查非主属性是否只依赖其中一部分。

对于:

Enroll(Sno, Sname, Dept, Cno, Cname, Teacher, Grade)
候选码:(Sno, Cno)

存在:

Sno → Sname, Dept
Cno → Cname, Teacher
(Sno, Cno) → Grade

这里多个非主属性部分依赖于联合码,所以该关系不满足2NF。

将只依赖学号、只依赖课程号、依赖完整选课组合的信息拆开:

Student(Sno, Sname, Dept)
Course(Cno, Cname, Teacher)
SC(Sno, Cno, Grade)

这样:

Student中的非主属性完全依赖于Sno;
Course中的非主属性完全依赖于Cno;
SC中的Grade完全依赖于(Sno, Cno)。

满足2NF后,如果不存在非主属性对候选码的传递依赖,则通常可认为满足3NF。

更规范地说,对于每一个非平凡函数依赖 X → Y,应至少满足:

X是超码;或Y是主属性。
Student(Sno, Sname, DeptNo, DeptName)

函数依赖:

Sno → Sname, DeptNo
DeptNo → DeptName

候选码为 Sno,不存在部分依赖,因此它可以满足2NF。但是:

Sno → DeptNo → DeptName

DeptName 传递依赖于主码,所以不满足3NF。

Student(Sno, Sname, DeptNo)
Department(DeptNo, DeptName)

这样院系名称只存放在院系表中,修改院系名称时不会产生多行不一致。


如果关系模式中的每一个非平凡函数依赖 X → Y 的决定因素 X 都是超码,则该关系满足 BCNF。

直观记忆:

BCNF要求更严格:凡是能够决定其他属性的属性组,都必须足够强,能够唯一确定整行。
满足BCNF一定满足3NF;
满足3NF不一定满足BCNF。

假设:

Teaching(Student, Course, Teacher)

业务规则:

一个学生选一门课对应一位教师:(Student, Course) → Teacher
一位教师只负责一门课程:Teacher → Course

候选码可能包括:

(Student, Course)
(Student, Teacher)

依赖 Teacher → Course 中,Teacher 并不是超码,因此该模式不满足BCNF。它可分解为:

TeacherCourse(Teacher, Course)
StudentTeacher(Student, Teacher)

9. 多值依赖与第四范式的了解内容

Section titled “9. 多值依赖与第四范式的了解内容”

教材的规范化章节还涉及多值依赖。例如一个课程可能独立对应多个教师和多本参考书:

CourseTeacherBook(Course, Teacher, Book)

如果教师集合与参考书集合互不依赖,就会组合产生大量重复元组。可表示为:

Course →→ Teacher
Course →→ Book

可分解为:

CourseTeacher(Course, Teacher)
CourseBook(Course, Book)

闭卷范围通常更核心的是函数依赖与1NF、2NF、3NF、BCNF,但如果题目出现“多值依赖”或“第四范式”,应知道它关注的是彼此独立的多值事实引起的冗余。


分解后,将子关系自然连接起来,应能恢复原关系中的真实信息,并且不能产生额外的错误元组。

例如:

Student(Sno, Sname, Dept)
SC(Sno, Cno, Grade)

它们通过 Sno 连接,可以恢复学生与其成绩信息。若分解时丢失了用于连接的关键属性,就可能无法正确恢复。

对于分解 R → R1, R2,常用判断思路是:

若交集属性 R1 ∩ R2 能函数决定 R1 或 R2 中的全部属性,则该二元分解通常为无损连接。

分解后,原来的重要函数依赖应尽可能在各个子关系内部直接检查,而不需要先把多张表连接起来才能验证。

例如分解后:

Student(Sno, Sname, Dept)
Course(Cno, Cname, Teacher)
SC(Sno, Cno, Grade)

原有依赖:

Sno → Sname, Dept
Cno → Cname, Teacher
(Sno, Cno) → Grade

均可以在某一张子表中直接检查,因此依赖得到保持。


拿到关系模式和函数依赖集后,可以按下面流程答题。

从题意提取:

编号决定什么;
联合编号决定什么;
某个业务属性是否能决定另一个属性。

找出能够推出全部属性、并且不能再删去任何一个属性的最小属性组。

第三步:区分主属性与非主属性

Section titled “第三步:区分主属性与非主属性”

出现在候选码中的属性为主属性,其余为非主属性。

顺序判断:

属性是否原子化 → 1NF
是否存在非主属性对联合码的部分依赖 → 2NF
是否存在非主属性对码的传递依赖 → 3NF
是否所有决定因素都是超码 → BCNF

结合业务说明:

数据重复在哪里;
新增某类信息为何无法插入;
修改某信息为何需改多行;
删除某记录为何会误删其他信息。

把每一类“由决定因素确定的事实”放到独立关系中,同时保留业务联系表。

至少说明:

消除了哪些部分依赖或传递依赖;
分解后各表主键是什么;
重要依赖是否仍能检查;
连接是否能够还原原有业务信息。

给定关系:

Borrow(Sno, Sname, BookNo, BookName, Publisher, BorrowDate, ReturnDate)

业务规则:

Sno → Sname
BookNo → BookName, Publisher
(Sno, BookNo, BorrowDate) → ReturnDate

一次借阅由学生、图书和借阅日期共同确定,因此候选码为:

(Sno, BookNo, BorrowDate)
Sname只依赖Sno,是对联合码的部分依赖;
BookName和Publisher只依赖BookNo,也是部分依赖;
因此原关系不满足2NF。
Student(Sno, Sname)
Book(BookNo, BookName, Publisher)
BorrowRecord(Sno, BookNo, BorrowDate, ReturnDate)
学生姓名只在Student中保存一次;
图书基本信息只在Book中保存一次;
BorrowRecord仅记录一次借阅事实;
消除了由于重复保存学生和图书信息造成的更新、插入和删除异常。

易混内容正确理解
主键与候选码候选码可以有多个,主键是被选择的一个候选码
部分依赖只在联合候选码场景下重点出现,非主属性只依赖码的一部分
传递依赖非主属性通过另一个非主属性间接依赖于码
2NF与3NF2NF消除部分依赖,3NF进一步消除非主属性的传递依赖
3NF与BCNFBCNF更严格,所有决定因素都必须是超码
分解不是随便拆表还要考虑无损连接与函数依赖保持

规范化是利用函数依赖等理论改进关系模式的过程,目的在于减少数据冗余,避免插入异常、删除异常和更新异常。
函数依赖X→Y表示属性组X的值能够唯一确定属性组Y的值。候选码是能够唯一确定全部属性且不可再约简的属性组;出现在候选码中的属性称为主属性。
1NF要求属性值不可再分;2NF要求在满足1NF的基础上,非主属性完全依赖于候选码,消除部分依赖;3NF进一步消除非主属性对候选码的传递依赖;BCNF要求每一个非平凡函数依赖的决定因素都是超码。
关系模式分解时既要提高范式,也应考虑无损连接和保持函数依赖,使分解后的表能够正确还原业务数据,并方便检查原有约束。