关系型数据库设计时,遵照一定的规范要求,目的在于降低数据的冗余性和数据的一致性,目前业界范式有:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、斯-科德范式(BCNF)、第四范式(4NF)、第五范式(5NF)。
范式的标准定义是:符合某一种级别的关系模式的集合,表示一个关系内部各属性之间的联系的合理化程度。通俗地讲,范式可以理解为一张数据表的表结构所符合的某种设计标准的级别。
使用范式的根本目的是:减少数据冗余,尽量让每个数据只出现一次,获取数据时通过 join 拼接出最后的数据。
表 1-1 学生成绩表
学号 |
姓名 |
系名 |
系主任 |
课名 |
分数 |
20170901176 |
王小强 |
计算机系 |
马小腾 |
C 语言 |
95 |
20170901176 |
王小强 |
计算机系 |
马小腾 |
计算机基础 |
99 |
20170901176 |
王小强 |
计算机系 |
马小腾 |
高等数学 |
80 |
20170901179 |
李阳 |
经管系 |
王小石 |
经济学 |
95 |
20170901179 |
李阳 |
经管系 |
王小石 |
管理学 |
92 |
20170901186 |
张小俊 |
数学系 |
钱小森 |
高等数学 |
89 |
20170901186 |
张小俊 |
数学系 |
钱小森 |
线性代数 |
96 |
若在一张表中,在属性(或属性组)X 的值确定的情况下,必定能确定属性 Y 的值, 那么就可以说 Y 函数依赖于 X,写作 X → Y。
也就是说,在数据表中,如果符合函数依赖,那么不存在任意两条记录,它们在 X 属性(或属性组)上的值相同,而在 Y 属性上的值不同。这也就是“函数依赖”名字的由来,类似于函数关系 y = f(x),在 x 的值确定的情况下,y 的值一定是确定的。
例如,对于表 1-1 中的数据,找不到任何一条记录,它们的学号相同而对应的姓名不同。所以我们可以说姓名函数依赖于学号,写作 学号 → 姓名。但是反过来,因为可能出现同名的学生,所以有可能不同的两条学生记录,它们在姓名上的值相同,但对应的学号不同, 所以我们不能说学号函数依赖于姓名。
表中其他的函数依赖关系还有如:
系名 → 系主任
学号 → 系主任
(学号,课名) → 分数
以下函数依赖关系则不成立: 学号 → 课名
学号 → 分数课名 → 系主任
(学号,课名) → 姓名
在一张表中,若 X → Y,且对于 X 的任何一个真子集(假如属性组 X 包含超过一个属性的话),X ' → Y 不成立,那么我们称 Y 对于 X 完全函数依赖,记做:
例如:
学号 F→ 姓名
(学号,课名) F→ 分数 (注:因为同一个的学号对应的分数不确定,同一个课名 对应的分数也不确定)
假如 Y 函数依赖于 X,但同时 Y 并不完全函数依赖于 X,那么我们就称 Y 部分函数依赖于 X,记做:
简单来说,(学号,课名)→ 系名,学号 → 系名,那么(学号,课名)p→ 系名。
假如 Z 函数依赖于 Y,且 Y 函数依赖于 X ,且 Y 不包含于 X,X 不函数依赖于 Y,那么我们就称 Z 传递函数依赖于 X,记做:
简单来说,系名 → 系主任,学号 → 系名,那么学号 T→ 系主任。
一范式(1NF):域应该是原子性的,即数据库表的每一列都是不可分割的原子数据项。
域:域就是列的取值范围,比如性别的域就是(男,女)
表 1-2 不符合一范式的表格设计
ID |
商品 |
商家ID |
用户ID |
001 |
5 台电脑 |
XXX旗舰店 |
0000 |
很明显如图 1-1 所示的表格设计是不符合第一范式的,商品列中的数据不是原子数据项,是可以进行分割的,因此对表格进行修改,让表格符合第一范式的要求,修改结果如图1-2 所示:
表 1-3 符合一范式的表格设计
ID |
商品 |
数量 |
商家ID |
用户ID |
001 |
电脑 |
5 |
XXX旗舰店 |
00001 |
实际上, 1NF 是所有关系型数据库的最基本要求, 你在关系型数据库管理系统
(RDBMS),例如 SQL Server,Oracle,MySQL 中创建数据表的时候,如果数据表的设计不符合这个最基本的要求,那么操作一定是不能成功的。也就是说,只要在 RDBMS 中已经存在的数据表,一定是符合 1NF 的。
二范式(2NF):在 1NF 的基础上,实体的属性完全函数依赖于主关键字(混合主键), 不能存在部分函数依赖于主关键字(混合主键)。
如果存在某些属性只依赖混合主键中的部分属性,那么不符合二范式。
表 1-4 不符合二范式的表格设计
学生 ID |
姓名 |
所属系 |
系主任 |
所修课程 |
分数 |
20170901176 |
王小强 |
计算机系 |
马小腾 |
000001 |
95 |
20170901176 |
王小强 |
计算机系 |
马小腾 |
000002 |
99 |
上述表格中是混合主键(学生 ID + 所修课程),但是所属系和系主任这两个属性只依赖于混合主键中的学生 ID 这一个属性,因此,不符合第二范式。
如果有一天学生的所属系要调整,那么所属系和系主任这两列都需要修改,如果这个学生修了多门课程,那么表中的多行数据都要修改,这是非常麻烦的,不符合第二范式。
为了消除这种部分依赖,只有一个办法,就是将大数据表拆分成两个或者更多个更小的数据表。
表 1-5 符合二范式的表格设计(1)
学生 ID |
所修课程 |
分数 |
20170901176 |
000001 |
95 |
20170901176 |
000002 |
99 |
学生 ID |
所属系 |
系主任 |
20170901176 |
计算机系 |
马小腾 |
20170901176 |
计算机系 |
马小腾 |
学生 ID |
姓名 |
20170901176 |
王小强 |
20170901176 |
王小强 |
通过上述的修改,当一个学生的所属系需要调整时,不管学生修了多少门课程,都只需要改变表 1-5 中的一行数据即可。
3NF 在 2NF 的基础之上,消除了非主属性对于主键(复合主键)的传递依赖。
表 1-6 不符合三范式的表格设计
订单ID |
商品ID |
商品颜色 |
商品尺寸 |
商家ID |
用户ID |
001 |
0001 |
深空灰 |
300*270*40 |
XXX旗舰店 |
00001 |
很明显,表 1-7 中,商品颜色依赖于商品 ID,商品 ID 依赖于订单 ID,那么非主属性商品颜色就传递依赖于订单 ID,因此不符合三范式,解决方案是将大数据表拆分成两个或者更多个更小的数据表。
表 1-7 符合三范式的表格设计(1)
订单ID |
商品ID |
商家ID |
用户ID |
001 |
0001 |
XXX旗舰店 |
00001 |
商品ID |
商品颜色 |
商品尺寸 |
0001 |
深空灰 |
300*270*40 |