文章参考:
文章目录
1. 函数依赖(functional dependency,FD)
记 A->B
表示 A 函数决定 B
,也可以说 B 函数依赖于 A
。
在一个关系中,任意元组,若属性 A1,A2…An 一样,则属性 B1,B2…Bm 必一样,那么称 A1,A2…An 函数决定 B1,B2…Bm。
记号为 A1,A2...An → B1,B2...Bm
Ai与Bi有函数依赖)
如果 {A1,A2,... ,An}
是关系的一个或多个属性的集合,该集合函数决定了关系的其它所有属性并且是最小集合,那么该集合就称为 键码。
对于 A->B
,如果能找到 A 的真子集 A’,使得 A'-> B
,那么 A->B 就是 部分函数依赖,否则就是 完全函数依赖。
对于 A->B,B->C
,则 A->C
是一个传递函数依赖。
2. 异常
以下的学生课程关系的函数依赖为 {Sno, Cname} -> {Sname, Sdept, Mname, Grade},键码为 {Sno, Cname}。也就是说,确定学生和课程之后,就能确定其它信息。
![](https://i-blog.csdnimg.cn/blog_migrate/050f8a6432948047bcb875eb86a3ed0d.png)
不符合范式的关系,会产生很多异常,主要有以下四种异常:
- 冗余数据:例如 学生-2 出现了两次。
- 修改异常:修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改。
- 删除异常:删除一个信息,那么也会丢失其它信息。例如删除了 课程-1 需要删除第一行和第三行,那么 学生-1 的信息就会丢失。
- 插入异常:例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。
3. 范式
范式理论是为了解决以上提到四种异常。
高级别范式的依赖于低级别的范式,1NF 是最低级别的范式。
第一范式 (1NF)
定义
属性不可分。可以认为任何表都属于第一范式,因为每个表的最小单位为表中的各个属性。
依赖关系
一般在分解之前,我们先求出所有候选键,从中挑选出一个作为主键,然后最小化函数依赖基本集。
键
(key):由关系的一个或多个属性组成,任意两个键相同的元组,所有属性都相同。需要保证表示键的属性最少。一个关系可以存在好几种键,工程中一般从这些候选键中选出一个作为主键
(primary key)。候选键
(prime attribute):由关系的一个或多个属性组成,候选键都具备键的特征,都有资格成为主键。
以下面表结构为例:
候选键为 学号、课程、兴趣爱好 和 学号、授课主任、兴趣爱好,我们一般选一个作为主键。
即学号、课程、兴趣爱好(学号、授课主任、兴趣爱好)中任意两个组合都可以唯一决定一个元组/行
学生信息{学号,姓名,班级,兴趣爱好,班主任,课程,授课主任,分数}
依赖关系:
学号 → 姓名,班级 ;
班级 → 班主任 ;
学号,课程 → 分数 ;
课程 → 授课主任 ;
授课主任 → 课程
![](https://i-blog.csdnimg.cn/blog_migrate/854d808f7acb74ee5ef8df1596557fe4.png)
第二范式 (2NF)
定义
在满足第一范式前提下,在所有函数依赖表达式中,不存在 任何 候选键的真子集 决定 非主属性,
即消除 非主属性 对于 键 的 部分依赖。
分解依赖关系
还是使用上面的表:
候选键:{学号,兴趣爱好,课程}、{学号,兴趣爱好,授课主任}
主属性:学号、兴趣爱好、课程、授课主任(候选键中的属性)
非主属性:班级、姓名、班主任、分数
由于存在以下依赖关系
- 学号 → 姓名 ;
- 学号 → 班级 ;
- 学号 → 班级 → 班主任 ; (传递依赖)
- 学号,课程 → 分数
非主属性 由 候选键 的一部分决定,因此不满足第二范式,需要分解。
根据上面画的结构图分解为如下三张表:
- 学生信息 {学号,姓名,班级,班主任}
候选键:{学号} (原表的候选键 :{学号,兴趣爱好,课程}、{学号,兴趣爱好,授课主任})
主属性:学号 (原表的主属性:学号、兴趣爱好、课程、授课主任)
非主属性:姓名、班级、班主任 (原表的非主属性:班级、姓名、班主任、分数)
函数依赖在这上面的投影为:
学号 → 姓名 ;
学号 → 班级 ;
班级 → 班主任
由于
学号 → 姓名 ;
学号 → 班级 ;
学号 → 班级 → 班主任
非主属性由 候选键(而非键的一部分)决定,因此满足第二范式。
- 学生课程信息 {学号,课程,授课主任,分数}
候选键:{学号,课程}、{学号,授课主任}
主属性:学号、课程、授课主任
非主属性:分数
函数依赖在这上面的投影为:
学号,课程 → 分数 ;
由于非主属性由键(而非键的一部分)决定,因此满足第二范式。
- 学生课程及爱好信息 {学号,课程,兴趣爱好}
候选键:{学号,兴趣爱好,课程}
主属性:学号、兴趣爱好、课程
非主属性:无
函数依赖在这上面的投影为:无
由于非主属性(这里没有非主属性,必然满足)由键(而非键的一部分)决定,因此满足第二范式。
最终得到的分解结果如下:
对于分解结果:
-
对于冗余:极大地减少了数据的重复次数,不过仍然存在冗余
-
对于更新异常:若更换了13班的班主任,仅需要访问两个元组,比之前方便了点。
-
对于删除异常:若小明英语缺考,我们需删除学生课程信息的第二行,但同时也删除了赵英的信息,有风险。
第三范式(3NF)
定义
在满足第二范式前提下,在所有函数依赖表达式中,所有 非主属性 不传递依赖于 候选键,这里A → B , B → C则称C传递依赖于A,
分解依赖关系
对于2NF分解结果中的第一个表学生信息 {学号,姓名,班级,班主任}
候选键:{学号}
主属性:学号
非主属性:姓名、班级、班主任
函数依赖:学号 → 姓名 ; 学号 → 班级 ; 班级 → 班主任
由于 学号 → 班级 → 班主任
存在 非主属性 传递依赖于 候选键,因此不满足第三范式,需要分解。
![](https://i-blog.csdnimg.cn/blog_migrate/c312eaf53c878054960a4f76dbbb7bf2.png)
该表分解为如下两张表:
- 学生信息 {学号,姓名,班级}
候选键:{学号}
主属性:学号
非主属性:姓名、班级
函数依赖在这上面的投影为:
学号 → 姓名 ;
学号 → 班级
由于 不存在非主属性传递依赖于候选键,因此满足第三范式。
- 班主任信息 {班级,班主任}
候选键:{班级}
主属性:班级
非主属性:班主任
函数依赖在这上面的投影为:
班级 → 班主任
由于 不存在非主属性传递依赖于候选键,因此满足第三范式。
最终得到的分解结果如下:
分解效果:
-
对于冗余:极大地减少了数据的重复次数
-
对于更新异常:若更换了13班的班主任,仅需要访问一个元组,该异常消除了,但如果更换授课主任任然有异常。
-
对于删除异常:若删除某个班主任,仅访问一个元组,不存在风险。