学习完函数依赖之后我们就可以以函数依赖的定义为依据来规范设计数据库了。范式(normal form)的意思是规范的程度,如第一范式第二范式第三范式,数字越大说明规范程度越高。让数据库满足某一范式实际上是在消除函数依赖的过程,但不是指满足任一范式的数据库就没有了函数依赖。
第一范式1NF
上表中很明显联系方式包含电话和邮箱,经过第一范式设计之后可以变成:
可以看到将原来的属性——联系方式分解成了新的两个属性——手机号、邮箱。因此第一范式要求数据库中每一个属性都不可再分,即每一个属性如原子一般不可再被分割,这是关系数据库种类中对数据库的最低要求。同样,第一范式还要求属性不可重复,因此总结:第一范式要求属性原子化、不可重复。
第二范式2NF
当一个数据库满足了第一范式依旧存在其他问题。比如下表,是满足第一范式的SLC表,属性满足原子化、不可重复。
可以知道该关系的复合主键是(Sno,Cno),其他的非主属性对主键存在各种函数依赖。
考虑下面的问题:
- 一个学生不止修一门课,因此当我们单单假如一个学生和课程号的同时,所在系、宿舍都会被重新再写入新的元组中去——冗余
- 当新生入学还未选课,但已经分配宿舍和所在系,但Cno作为主键的一部分并不能为空——更新
- 当有学生所选的课已经结课,那么在删除Cno的同时,所在系和宿舍也会被删除——删除
这些问题其实很好解决,就是拆表,我们可以看到其实是因为所在系和宿舍跟复合主键(Sno,Cno)存在部分函数依赖,改成完全函数依赖就不会存在冗余、更新、删除的问题了。
拆成下面两个表,就可以消除上面由部分函数依赖带来的问题了。
因此,第二范式实际上是在消除非主属性对候选键的部分函数依赖问题,同时满足第一范式。
第三范式3NF
学生课程表(SC)
Sno (学号) | Cno (课程号) | Cname (课程名称) |
2021310721 | 101 | 计算机科学导论 |
2021310722 | 102 | 线性代数 |
2021310723 | 101 | 计算机科学导论 |
可以看到该关系满足第一范式同时不存在部分函数依赖。但依旧存在如下问题:
- 有多名学生选择同一个课程,那么在添加课程号的时候需要重复添加相同的课程名称——冗余
- 如果某个课程名字需要修改更新,那么很多处有原课程名称的地方都需要修改——修改
- 如果有新开设某一门课,但还未有学生选,那么会出现无法插入的情况——插入
这些问题的出现其实是因为非主属性对键存在传递函数依赖,Sno->Cno,Cno↛Sno,Cno->Cname,传递依赖:Sno → Cname,因此为了消除该传递依赖,我们对原关系进行拆解:
学生课程关联表(StudentCourses)
Sno (学号) | Cno (课程号) |
2021310721 | 101 |
2021310722 | 102 |
2021310723 | 101 |
课程表(Courses)
Cno (课程号) | Cname (课程名称) |
101 | 计算机科学导论 |
102 | 线性代数 |
因此第三范式是指满足第一、第二范式同时不存在非主属性对候选键的传递函数依赖(要求每一个非主属性都直接函数依赖于候选键)
巴斯-科德范式BCNF
考虑下面关系:每一个老师只教授一门课,每个学生可以选多门课,每门课可以有多个老师教授。
属性之间存在的函数依赖关系是
• (Sno, Cno)→ Tno, (Sno, Tno)→ Cno, Tno → Cno;
• 它的候选键是:(Sno, Cno)和 Sno,Tno
• 主属性是Sno, Cno, Tno
因为没有非主属性,所以该关系满足第三范式,即没有部分函数依赖、传递函数依赖。但依旧存在问题:
- 由于Tno → Cno,所以当一个老师开了门新课但是还没有学生选择,那么就不能插入——插入
- 由于主属性Cno 部分函数依赖于候选键(Sno,Tno),(是Tno → Cno)所以:
①当有多名学生选的都是同一个老师的同一门课,那么课程号和老师都需要重复添加——冗余
②当选修CS204这门课的学生毕业了,那么在删除该学生选课记录时会将老师任课信息也都删去——删除。
为了解决该问题——即主属性对候选键的部分函数依赖,我们这样拆解表:
满足了巴斯科德范式,前提是要满足第三范式,只不过加了一个主属性也必须直接函数依赖于候选键,而不存在传递函数依赖,所以也可以这么描述:满足第一范式的基础上,不存在任何属性对候选键的传递函数依赖,此范式和第三范式联系紧密:主要是因为①当一个关系只有一个候选键(一个主属性),而且此关系满足第三范式,那么将自动满足BCNF②当一个关系满足BCNF那么将自动满足第三范式。
到这,如果一个关系已经满足了BCNF,那么说明在函数依赖的范畴里,它的规范程度已经是最高的了,消除了删除异常和插入异常。
第四范式4NF
该范式是在满足BCNF的基础上消除了多值依赖。
上表中,存在多值依赖Sno→→Phone,Sno→→Email,
将其分解之后就得到:
第四范式不允许非平凡的、且非函数依赖的多值依赖,但允许存在非平凡多值依赖(实际上是函数依赖)。
解释:在原关系中,存在多值依赖Sno→→Phone,Sno→→Email,即存在非平凡的多值依赖,同时也是非函数依赖(指一个属性并不单只决定另一个属性值,而是一组),所以将其分解之后, 一个学号可以对应一个手机号,即使一个学生有很多手机号,但是这种元组记录就是函数依赖,因为可以实现一个属性值决定另一个。
总结
日常使用中一般分解到满足第三范式和BCNF,注意:越高范式冗余程度越低,而且高范式在低范式的基础之上。