规范化
关系数据库的规范化理论最早是由关系数据库理论创始人E.F.Codd提出的,后来又经过了许多专家和学者对其进行了深入的研究和发展完善,最后形成了一套关系数据库的设计理论。这套理论主要包括三个方面的内容:
1 函数依赖
2 范式
3 模式设计
本文要重点讨论的是范式,其余那两个方面的内容清参照任何一本关系数据库理论教材。
规范化的基本思想是企图消除关系模式中的数据冗余,消除数据依赖中的不合理部分,解决数据插入、删除时发生的概念性异常现象。数据冗余不必多说,下面举例说明数据插入和删除时发生概念性异常的情况。例:要求设计员工管理数据库,其关系模式如下:
R(员工姓名,部门,项目组)
插入异常:如果某个新设立的部门,尚未招聘员工的时候,则部门和项目组的信息无法插入。
删除异常:在某部门员工全部离职而尚未招聘的时候,删除全部记录则部门和项目组的信息也随之删除。而这实际上部门和项目组依然存在,但在数据库中却无法找到该部门的信息。
范式
要解决上述问题,这就要求设计出来的关系模式满足一定的条件。我们把关系数据库的规范化过程中为不同程度的规范化要求而设立的不同标准成为范式(Normal Form)。由于规范化的程度不同,就产生了不同的范式。范式的概念最早也是由E.F.Codd提出的。从1971年起,他相继提出了三级范式,即1NF、2NF、3NF。后来,又有人定义了4NF和5NF。其中,每种范式都规定了一些限制约束条件,一级比一级有更严格的要求。下面,以具体例子来介绍各个范式的定义和满足范式的做法。员工培训管理为例,刚开始的时候,可能定义数据表如下:
表1
员工名 | 项目组 | 项目主管 | 培训课程1 | 培训课程2 |
张三 | 网银 | 王五 | 程序设计 | 英语 |
李四 | 社保 | 赵六 | 数据库 | 英语 |
张三 | 嵌入式 | 孙七 | 软件架构 | 项目管理 |
这种没有经过处理的数据表形式,有很明显的缺点。比如,员工要加入第三个培训班级时,则需要新建一列。所以,要创建一个有扩展性的系统。采用遵守1NF的办法可以解决这个问题。
第1范式(1NF)
定义:如果关系模式R,其所有的属性均为简单属性,即每个属性都是不可再分的,则称R属于第一范式,简称1NF。
说明:1NF强调每一列必须为原子列且无概念性重复。比如,表1中虽然每列都是原子列,但【培训课程1】【培训课程2】列,在概念上是重复的列。所以,表1不符合1NF。
对策:消除表中重复列,为每套与重复列相关的数据建立一个独立的行。这样一来,解决了培训课程列的扩展性问题。通过以上遵守1NF的对策,得到了如下新表:
表2
员工名 | 项目组 | 项目主管 | 培训课程 |
张三 | 网银 | 王五 | 程序设计 |
张三 | 网银 | 王五 | 英语 |
李四 | 社保 | 赵六 | 数据库 |
李四 | 社保 | 赵六 | 英语 |
张三 | 嵌入式 | 孙七 | 软件架构 |
张三 | 嵌入式 | 孙七 | 项目管理 |
不过,这样仍有一个遗留问题和处理后带来的一个新问题,即,员工重名问题(网银组张三和嵌入式组张三必然是重名,但网银组的两行张三是一个人还是重名的两个人,难以断定)和数据冗余问题(每次为员工加入一门新培训课程的时候,都必须重复的插入员工名,项目组和项目主管的信息。这样一来,使数据出现冗余并容易出错)。因此,还需要遵守2NF来解决问题。
第2范式(2NF)
定义:如果关系模式R?1NF,且每个非主属性都完全函数依赖于R的每个关系键,则称R属于第二范式,简称2NF。
说明:2NF要求数据库表中的每行必须可以被惟一地区分,并且要求实体的属性完全依赖于主属性。
对策1:使用主键来标识相关数据。第1范式主要讨论了非主属性与主属性的关系问题,只有表中存在了主属性标识,才能继续这个问题。而现在表2中,由于重名问题没有解决,并无可以标识主属性的一列或者几列。下面通过加入员工号来标识主属性:
表3
员工号 | 员工名 | 项目组 | 项目主管 | 培训课程 |
001 | 张三 | 网银 | 王五 | 程序设计 |
001 | 张三 | 网银 | 王五 | 英语 |
002 | 李四 | 社保 | 赵六 | 数据库 |
002 | 李四 | 社保 | 赵六 | 英语 |
003 | 张三 | 嵌入式 | 孙七 | 软件架构 |
003 | 张三 | 嵌入式 | 孙七 | 项目管理 |
至此,表3中已经有了能标识主属性的列,即主键:员工号。
对策2:消除不完全依赖于主键的非主属性,为其建立独立的新表,使用外键来与其关联。假设企业的体制是,一个员工有唯一的名字、项目组、项目主管。那么,一旦确定了一个员工号,则员工名、项目组、项目主管也就被确定了。即,符合变量单值函数 y = f(x) 这种关系,则可以说员工名、项目组、项目主管函数依赖与主键员工号(在主键是多个列的时候才能讨论是完全依赖还是部分依赖的问题,单列主键的情况下,函数依赖必是完全函数依赖),但一个员工可能有多个培训课程与其对应,因此培训课程不能被唯一确定,即培训课程不能函数依赖于员工号。通过对策2,把表3分解为两个表,表4-1和4-2。
表4-1
员工号 | 员工名 | 项目组 | 项目主管 |
001 | 张三 | 网银 | 王五 |
002 | 李四 | 社保 | 赵六 |
003 | 张三 | 嵌入式 | 孙七 |
表4-2
rectId | 员工号 | 培训课程 |
1 | 001 | 程序设计 |
2 | 001 | 英语 |
3 | 002 | 数据库 |
4 | 002 | 英语 |
5 | 003 | 软件架构 |
6 | 003 | 项目管理 |
至此,一个员工加入新的培训课程时,已经不用重复加入项目组信息了,但是仍有潜在的冗余问题存在。即,假设每个项目组有唯一的主管,在某个项目组新进员工选择了一个课程时(例如,网银项目组新进员工朱八),要向表4-1中插入朱八的信息,这时,网银组主管王五就会被重复插入,出现了冗余。下面的第三范式将解决这个问题。
第3范式(3NF)
定义:如果关系模式R?2NF,且每个非主属性都不传递依赖于R的每个关系键,则称R属于第三范式(Third Normal Form),简称3NF。
说明:3NF致力于消除只是传递依赖与主键的非主属性。简而言之,就是属性不依赖于其它非主属性。在表4-1中,虽然项目主管完全函数依赖于主键员工号(即,一个员工号能唯一确定他的项目主管),但在概念上这只是传递依赖而并非直接依赖。项目主管直接依赖的是项目组,因为在概念上,一个项目组才是项目主管存在的唯一原因。正是因为一个员工号能唯一确定一个项目组,而一个项目组能唯一确定一个项目主管,所以说,项目主管对主键员工号的依赖关系是传递依赖,而不是直接依赖。它直接依赖的是非主属性项目组。
对策:为传递依赖于主键的属性建立独立的新表从而消除非主属性对主键的传递依赖关系。如此,将表4-1划分为以下两个表。
表5-1
员工号 | 员工名 | 所在项目组编号 |
001 | 张三 | P001 |
002 | 李四 | P002 |
003 | 张三 | P003 |
表5-2
项目组编号 | 项目组名 | 项目主管 |
P001 | 网银 | 王五 |
P002 | 社保 | 赵六 |
P003 | 嵌入式 | 孙七 |
至此,项目主管信息重复的问题被解决。但还有一个遗留问题,即,表4-2中,培训课程名依然有重复。将通过第四范式来解决这个问题。下面简要介绍第四范式。
第4范式(4NF)
概要:在多值依赖关系(多对多)中,独立的实体概念不能禁止存放在同一个表中。表4-2违反了这个规则,问题是,如果删除了员工号为001的员工,则降导致程序设计课程的信息丢失。
对策:为培训课程建立独立的表,为培训成员建立独立的表。
表5-3
培训课程编号 | 课程名 |
T001 | 程序设计 |
T002 | 英语 |
T003 | 数据库 |
T004 | 软件架构 |
T005 | 项目管理 |
表5-4
rectId | 培训课程编号 | 参加者员工号 |
1 | T001 | 001 |
2 | T002 | 001 |
3 | T002 | 002 |
4 | T003 | 002 |
5 | T004 | 003 |
6 | T005 | 003 |
表5-1~5-4这四个表即是最终结果。至此,本次介绍全部结束。最后,我要说的是:
在实践中不管什么情况,都严格恪守设计范式来进行数据库设计 —— 不推荐
完全没有研究过设计范式就进行数据库设计 —— 极其不推荐