函数依赖

记 A->B 表示 A 函数决定 B,也可以说 B 函数依赖于 A。

对于 A->B,如果能找到 A 的真子集 A’,使得 A’-> B,那么 A->B 就是部分函数依赖,

比如:(学号,学生姓名)-> 院系

​ 但也可以直接:学号 -> 院系


否则就是完全函数依赖。

比如:(学号,课程号)-> 成绩

​ 不可拆开


另一种解释:对于(A,B,C)

有(A,B)->C,且C不能单独通过A或B得到,则该依赖为完全函数依赖。

有(A,B)->C,且C可以单独通过A或B得到,则该依赖为部分函数依赖。(会造成冗余)

有 A->B,B->C,则 A->C 是一个传递函数依赖。(会造成冗余)





异常

以下的学生课程关系中,{学号, 课程号} 为键码,有如下函数依赖:

学号 -> 学生姓名,院系

院系 -> 院长姓名

课程号 -> 课程名

(学号,课程号) -> 成绩

学号 学生姓名 院系 院长姓名 课程号 课程名 成绩
学生1 学生A 学院1 院长A 课程1 课程A 90
学生2 学生B 学院2 院长B 课程1 课程A 80
学生2 学生B 学院2 院长B 课程2 课程B 100
学生3 学生C 学院3 院长C 课程2 课程B 95


不符合范式的关系,会产生很多异常,主要有以下四种异常:

  • 冗余数据:例如 学生B 出现了两次。
  • 修改异常:修改学生B 一条记录中的某个信息,但另一条记录中相同的位置却没被修改。
  • 删除异常:删除学生B 一条记录中的某个信息,那么该条记录中的其他信息也会丢失。
  • 插入异常:例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。





范式

范式是为了消除重复数据减少冗余数据,让磁盘空间得到更有效利用的一种标准。

高级别范式的满足低级别的范式,比如2NF一定满足1NF。


1. 第一范式 (1NF)

属性不可分,均为简单属性。但四种异常均无法解决。究其原因是其中存在各种各样的函数依赖关系。

例子中的关系就是一个1NF。



2. 第二范式 (2NF)

满足1NF,并且每个非主属性完全函数依赖于键码。

可以通过分解来满足。

分解前

{学号, 课程号} 为键码。有如下函数依赖:

学号 -> 学生姓名,院系

院系 -> 院长姓名

课程号 -> 课程名

(学号,课程号) -> 成绩

  • 成绩完全依赖于键码,它没有任何冗余数据,每个学生的每门课都有特定的成绩。

  • 学生姓名院系课程名部分依赖于键码,当一个学生选修了多门课时,这些数据就会出现多次,造成大量冗余数据。

  • 院长姓名传递依赖于键码。 (2NF不考虑)


分解后

关系1

学号-> 学生姓名, 院系

院系-> 院长姓名


关系-2

学号, 课程号-> 成绩


关系-3

课程号 -> 课程名



3. 第三范式 (3NF)

满足1NF,并且非主属性不传递函数依赖于键码。

上面的 关系1 中存在以下传递函数依赖:

学号-> 院系-> 院长姓名

可以进行以下分解:

关系-11

学号-> 学生姓名, 院系


关系-12

院系-> 院长姓名





背诵版

对象拆分的好,基本上就满足三大范式了。

  • 第一范式,字段不可拆分,原子性
  • 第二范式,联合主键情况,字段都应与主键有关联。如成绩表,主键学生id,学科id,不应该出现学生班级信息之类的字段。
  • 第三范式,表不包含多余属性,学生属性属于学生,班级属性属于班级。