原文详见:https://www.zhihu.com/question/24696366/answer/29189700 原文作者:刘慰
1.第一范式
第一范式要求关系中的每个属性不可再分,下图就不符合1NF
修改为下图后则满足1NF
2.第二范式
我们先来看这么一个例子,有如下这样一个表:
仔细查看,容易发现这么几个问题:
1.每一名学生的学号、姓名、系名、系主任这些数据重复多次。每个系与对应的系主任的数据也重复多次——数据冗余
2.假如学校新建了一个系,但是暂时还没有招收任何学生,那么是无法将系名与系主任的数据单独地添加到数据表中去的——插入异常
3.假如将某个系中所有学生相关的记录都删除,那么所有系与系主任的数据也就随之消失了(一个系所有学生都没有了,并不表示这个系就没有了)。——删除异常
4.假如李小明转系到法律系,那么为了保证数据库中数据的一致性,需要修改三条记录中系与系主任的数据。——修改异常。
然后,我们来看看第二范式的作用:在1NF的基础上,2NF消除了非主属性对于码的部分依赖。首先,我们需要理解三个基本概念:码(关键字),主属性/非主属性和函数依赖
码(关键字):关系中的某个属性或者某几个属性的组合称为候选码(候选关键字),简称码/关键字,用于区分每个元组
主属性/非主属性:码(关键字)中的属性为主属性,其余为非主属性
函数依赖(非严格的定义):若在一张表中,在属性(或属性组)X的值确定的情况下,必定能确定属性Y的值,那么就可以说Y函数依赖于X,写作 X → Y
完全函数依赖:在一张表中,若 X → Y,且对于 X 的任何一个真子集(假如属性组 X 包含超过一个属性的话),X ' → Y 不成立,那么我们称 Y 对于 X 完全函数依赖
如:(学号,姓名)->分数
部分函数依赖:假如 Y 函数依赖于 X,但同时 Y 并不完全函数依赖于 X,那么我们就称 Y 部分函数依赖于 X,记作 X P→ Y
如:(学号,课名)->姓名
传递函数依赖:假如 Z 函数依赖于 Y,且 Y 函数依赖于 X (严格来说还有一个X 不包含于Y,且 Y 不函数依赖于Z的前提条件),那么我们就称 Z 传递函数依赖于 X
这样,我们容易判断出上表中存在(学号,课名)->姓名,有学号->姓名,存在非主属性姓名对码(学号,课名)的部分函数依赖
对于(学号,课名)->系名,有学号->系名,存在非主属性系名对码(学号,课名)的部分函数依赖
对于(学号,课名) → 系主任,有 学号 → 系主任,存在非主属性 对码(学号,课名)的部分函数依赖。
对于(学号,课名) → 系主任,有 学号 → 系主任,存在非主属性 对码(学号,课名)的部分函数依赖。
这个问题我们通过模式分解进行解决,其中一种分解如下:
选课(学号,课名,分数)
学生(学号,姓名,系名,系主任)
这样就不存在非主属性对码的部分依赖问题了
3.第三范式
3NF在2NF的基础之上,消除了非主属性对于码的传递函数依赖。
选课(学号,课名,分数)
学生(学号,姓名,系名)
系(系名,系主任)
- 删除某个系中所有的学生记录
该系的信息不会丢失。——有改进 - 插入一个尚无学生的新系的信息。
因为系表与学生表目前是独立的两张表,所以不影响。——有改进 - 数据冗余更加少了。——有改进
4.BCNF范式
我们还是先来看一个问题:
仓库(仓库名,管理员)
库存(仓库名,物品名,数量)
这样,之前的插入异常,修改异常与删除异常的问题就被解决了。