通过规范来解决数据冗余以及一些操作异常的情况。在实际生活中,追求最高的范式并不一定是最好的选择,要考虑多方面因素,如查询速度,数据量等等,尽量选择一个满足需求的,合理的结构
目前关系数据库有六种范式:1NF,2NF,3NF,BCNF,4NF,5NF
第一范式(1NF)
要求数据库表的每一列都是不可分割的原子数据项
,即实体中的某个属性有多个值时,必须拆分为不同的属性,例如:
用户信息表
编号 | 姓名 | 年龄 | 地址 |
---|---|---|---|
1 | 小王 | 23 | 浙江省杭州市拱墅区湖州街51号 |
当实际需求对地址没有特定的要求下,这个用户信息表的每一列都是不可分割的。但是当实际需求对省份或者城市有特别要求时,这个用户信息表中的地址就是可以分割的,改为:
用户信息表
编号 | 姓名 | 年龄 | 省份 | 城市 | 详细地址 |
---|---|---|---|---|---|
1 | 小王 | 23 | 浙江省 | 杭州市 | 拱墅区湖州街51号 |
好处:
- 表结构相对清晰
- 易于查询
第二范式(2NF)
主键与非主键之间是完全函数依赖关系
。即数据库表中的每一列都和主键的所有属性相关,不能只和主键的部分属性相关
完全函数依赖:有属性集 X,Y,通过 X 中的所有属性能够推出 Y 中的任意属性,但是 X 的任何真子集,都不能推出 Y 中的任何属性,例如:
学生课程表
学生编号 | 课程编号 | 学生名称 | 课程名称 | 所在班级 | 班主任 |
---|---|---|---|---|---|
S1 | C1 | 小王 | 计算机导论 | 计算机3班 | 陈老师 |
S1 | C2 | 小王 | 数据结构 | 计算机3班 | 陈老师 |
S2 | C1 | 小马 | 计算机导论 | 软件1班 | 李老师 |
将学生编号和课程编号作为主键,能确定唯一一条数据,但是学生名称只跟学生编号有关,跟课程编号无关,即不满足完全函数依赖,改为:
学生表
学生编号 | 学生名称 | 所在班级 | 班主任 |
---|---|---|---|
S1 | 小王 | 计算机3班 | 陈老师 |
S2 | 小马 | 软件1班 | 李老师 |
课程表
课程编号 | 课程名称 |
---|---|
C1 | 计算机导论 |
C2 | 数据结构 |
学生课程关系表
学生编号 | 课程编号 |
---|---|
S1 | C1 |
S1 | C2 |
S2 | C1 |
好处:
- 相对节约空间,当学生表和课程表属性越多,效果越明显
- 解决插入异常,当新增一门课程时,原表因为没有学生选课,导致无法插入数据
- 解决更新繁琐,当更改一门课程名称时,原表要更改多条数据
- 解决删除异常,当学生学完一门课,原表若要清空学生上课信息,课程编号与课程名称的关系可能会丢失
第三范式(3NF)
任何非主属性不依赖于其它非主属性,即消除传递函数依赖
(在 2NF 基础上)
传递函数依赖:有属性集 X,Y,Z,满足 $X \Rightarrow Y,X \not\subset Y,Y \nRightarrow X,( X \bigcup Y ) \bigcap Z = \emptyset,Y \Rightarrow Z$ 时,则 Z 传递函数依赖于 X,例如:
学生表
学生编号 | 学生名称 | 所在班级 | 班主任 |
---|---|---|---|
S1 | 小王 | 计算机3班 | 陈老师 |
S2 | 小马 | 软件1班 | 李老师 |
学生编号作为主键满足第二范式(2NF)。通过学生编号 $\Rightarrow$ 所在班级 $\Rightarrow$ 班主任,所以班级和班主任之间存在依赖关系,改为:
学生表
学生编号 | 学生名称 | 所在班级 |
---|---|---|
S1 | 小王 | 计算机3班 |
S2 | 小马 | 软件1班 |
班级表
所在班级 | 班主任 |
---|---|
计算机3班 | 陈老师 |
软件1班 | 李老师 |
好处:
- 相对节约空间
- 解决更新繁琐
- 解决插入异常,当班级分配了老师,还没分配学生的时候,原表将不可插入数据
- 解决删除异常,当学生毕业后,若要清空学生信息,班级和老师的关系可能会丢失
巴斯-科德范式(BCNF)
在3NF基础上,消除主属性
之间的传递函数依赖
,例如:
配件表
仓库号 | 配件号 | 职工号 | 配件数量 |
---|---|---|---|
W1 | P1 | E1 | 10 |
W1 | P2 | E1 | 10 |
W2 | P1 | E2 | 20 |
有以下约束:
- 一个仓库有多个职工
- 一个职工只在一个仓库
- 一种配件可以放多个仓库
- 一个仓库,一个职工管理多个配件,一种配件由唯一一个职工管理
由此,将(仓库号,配件号)作为主键,满足 3NF,但是(仓库号,配件号)$\Rightarrow$ 职工号 $\Rightarrow$ 仓库号,造成传递函数依赖,改为:
仓库表
仓库号 | 职工号 |
---|---|
W1 | E1 |
W2 | E2 |
工作表
职工号 | 配件号 | 配件数量 |
---|---|---|
E1 | P1 | 10 |
E1 | P2 | 10 |
E2 | P1 | 20 |
好处:解决一些冗余和一些异常情况
不足:丢失一些函数依赖,如丢失(仓库号,配件号)$\Rightarrow$ 职工号,无法通过单表来确定一个职工号
第四范式(4NF)
当一个表中非主属性相互独立时(即在 3NF 基础上),这些非主属性不应该有多值
,例如:
客户联系方式
客户编号 | 固定电话 | 移动电话 |
---|---|---|
10 | 88-123 | 151 |
10 | 80-123 | 183 |
一个用户拥有多个固定电话和移动电话,给表的维护带来很多麻烦。比如增加一个固定电话,那么移动电话这一栏就较难维护,改为:
客户电话表
客户编号 | 电话号码 | 电话类型 |
---|---|---|
10 | 88-123 | 固定电话 |
10 | 80-123 | 固定电话 |
10 | 151 | 移动电话 |
10 | 183 | 移动电话 |
好处:解决一些异常,使表结构更加合理
第五范式(5NF)
在关系模式 R 中,每一个连接依赖
均有 R 的候选码
所隐含。即连接时,所连接的属性都是候选码,例如:
销售表
销售人员 | 供应商 | 产品 |
---|---|---|
S1 | V1 | P1 |
S2 | V2 | P2 |
S1 | V1 | P1 |
S2 | V2 | P2 |
要想找到某一条数据,必须以(销售人员,供应商,产品)为主键,改为:
销售人员_供应商表
销售人员 | 供应商 |
---|---|
S1 | V1 |
S2 | V2 |
销售人员_产品表
销售人员 | 产品 |
---|---|
S1 | P1 |
S2 | P2 |
供应商_产品表
供应商 | 产品 |
---|---|
V1 | P1 |
V2 | P2 |
好处:解决某些异常操作