数据库的设计范式是数据库所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入、删除和更新操作异常。反之则是乱七八糟,不仅给数据库的编程人员制造麻烦,而且面目可憎,可能存储了大量不需要的冗余信息。
首先看看各种键的定义:
超键:在关系中能唯一标识元组的属性集称为关系模式的超键。
候选键:不含有多余属性的超键称为候选键。
主键:用户选作元组标识的一个候选程序主键。
外键:如果关系模式R1中的某属性集不是R1的主键,而是另一关系R2的主键,则该属性集是关系模式R1的外键。
例:
假设有如下两个表:
学生(学号,性别,姓名,身份证号,教师编号)
教师(教师编号,姓名,工资)
超键:由超键的定义可知,学生表中含有学号和身份证号的任意组合都为此表的超键。如:(学号)、(学号,姓名)、(身份证号,性别)等等。
候选键:候选键属于超键,它是最小的超键,就是说在去掉候选键中的任何一个属性它就不是超键了,学生表中的候选键位:(学号)、(身份证号)。
主键:主键就是候选键里面的一个,是认为规定的,例如学生表中,我们通常会让(学号)做主键,教师表中让(教师编号)做主键。
外键:外键比较简单,学生表中的外键就是(教师编号)。外键主要是用来描述两个表的关系的。
1.1第一范式(1NF)无重复的列
所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个重复的值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原始体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例信息。简而言之,第一范式就是无重复的列。
说明:在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系型数据库。
例如:如下数据库表是符合第一范式的:
字段1 | 字段2 | 字段3 | 字段4 |
而这样的数据库表示不符合第一范式的:
字段1 | 字段2 | 字段3 | 字段4 | |
字段3.1 | 字段3.2 |
数据库表中的字段都是单一属性的,不可再分的,这一单一属性由基本类型构成,包括:整型、实数、字符型、逻辑型、日期型等。很显然,在当前的任何关系型数据库管理系统(DBMS)中,傻瓜也不可能做出不符合第一范式的数据库,因为这些DBMS不允许你把数据库表的一列再分成二列或多列。因此,你想在现有的DBMS中设计出不符合第一范式的数据库都是不可能的。
1.2第二范式(2NF)属性完全依赖于主键【消除部分子函数依赖】
如果关系模式R为第一范式,并且R中每一个非主属性完全函数依赖于R的某个候选键,则称为第二范式模式。
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被唯一的区分。为了区分通常需要为表加上一列,以存储各个实例的唯一标识。这个唯一属性列被称为主关键字或主键、主码。
例如员工信息表中加上了员工编号(emp_id)列,因为每个员工的员工编号是唯一的,因此每个员工可以被唯一标识。
简而言之,第二范式(2NF)就是非主属性完全依赖于主关键字。
所谓完全依赖是指不能存在仅依赖主关键字一部分的属性(设有函数依赖W->A,若存在XW,有X->A成立,那么称W->A是局部依赖,否则就称W->A是完全函数依赖)。如果存在,那么这个属性和主关键字的这一部分应该分离出来成为一个新的实体,新实体与原实体间是一对多的关系。
假定选课关系表为:SelectCourse(学号,姓名,年龄,课程名称,成绩,学分),关键字为组合关键字(学号,课程名称),因为存在如下决定关系:
(学号,课程名称)->(姓名,年龄,成绩,学分)
这个数据库表不满足第二范式(2NF),因为存在如下决定关系:
(课程名称)->(学分) , (学号)->(姓名,年龄)
即存在组合关键字中的字段决定非关键字的情况。
由于不符合2NF,这个选课关系表存在如下问题:
(1)数据冗余:
同一个课程由n个学生选修,“学分就重复n-1次”;同一个学生选修了m门课程,姓名和年龄就重复了m-1次。
(2)更新异常:
若调整了某门课程的学分,数据表中所有行的“学分”都要更新,否则会出现同一门课程学分不同的情况。
(3)插入异常:
假设要开设一门新的课程,暂时还没有人选修。这样,由于还没有“学号”关键字,课程名称和学分也无法录入数据库。
(4)删除异常:
假设一批学生已经完成课程的选修,这些选修记录就应当从数据库表中删除。但是,与此同时,课程名称和学分信息也被删除了。很显然,这也会导致插入异常。
把选课关系表SelectCourse改为如下三个表:
学生:Student(学号,姓名,年龄),课程:Course(课程名称,学分),选课关系:SelectCourse(学号,课程名称,成绩)。
这样数据库表示符合第二范式的,消除了数据冗余,更新异常,插入异常和删除异常。
另外,所有单关键字的数据库表都是符合第二范式的,因为不可能存在组合关键字。
1.3第三范式(3NF)属性不依赖于其他非主属性(消除传递依赖)
如果关系模式R是第二范式的,且每个非主属性都不依赖于R的候选键,则称R为第三范式模式。
满足第三范式(3NF)必须先满足第二范式(2NF)。第三范式(3NF)要求一个数据库表中不包含那些在其他表中已包含的非主关键字。
例如:存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能在将部门名称、部门简介等部门有关的信息在加入到员工信息表。如果不存在部门信息表,则根据第三范式(3NF)也应当构建它,否则就会有大量的数据冗余。
第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字对任一候选关键字段的传递函数依赖则符合第三范式。简而言之,第三范式就是属性不依赖其他非主属性。
所谓传递函数依赖是指:如果存在“A->B->C”决定关系,则C传递函数依赖于A。
因此,满足第三范式的数据库表不应该存在如下依赖关系:
关键字段->非关键字段X->非关键字段Y
假定学生关系表为Student(学号,姓名,年龄,所在学院,学院地点,学院电话),关键字为单一关键字“学号”,应为存在如下决定关系:
(学号)->(姓名,年龄,所在学院,学院地点,学院电话)
这个数据库表示符合第二范式(2NF)的但是不符合第三范式(3NF),应为存在如下决定关系:
(学号)->(所在学院)->(学院地点,学院电话)
即存在非关键字段“学院地点”、“学院电话”对关键字段“学号”的传递函数依赖。它也会存在数据冗余、更新异常、插入异常和删除异常。
把学生关系表分成如下两个表:
学生:Student(学号,姓名,年龄,所在学院), 学院:Academy(学院,地点,电话)
这样的数据库表示符合第三范式的,消除了数据冗余、更新异常、插入异常和删除异常。
1.4鲍依斯-科得范式(BCNF,3NF的改进形式)
若关系模式R是第一范式,且每个属性都不传递依赖于R的候选键。这种关系模式就是BCNF模式。即在第三范式的基础上,数据库表中如果不存在任何字段对任一候选关键字段的传递函数依赖则符合鲍依斯-科得范式。
假设仓库管理关系表为:StorehourseManage(仓库ID,存储物品ID,管理员ID,数量),且有一个管理员只在一个仓库工作;一个仓库可以存储多种物品,这个数据库表中存在如下决定关系:
(仓库ID,存储物品ID)->(管理员ID,数量),(管理员ID,存数物品ID)->(仓库ID,数量)
所以,(仓库ID,存储物品ID)和(管理员ID,存储物品ID)都是Storehoursemanager的候选关键字,表中的唯一非关键字段为数量,它是符合第三范式的,但是由于存在如下决定关系:
(仓库ID)->(管理员ID) , (管理员ID)->(仓库ID)
即存在关键字决定关键字的情况,所以不符合BCNF范式。它会出现如下的异常:
(1)删除异常
当仓库被清空后,所有“存储物品ID”和“数量”信息被删除的同时,“仓库ID”和“管理员ID”也被删除了。
(2)插入异常
当仓库没有任何物品的时候,无法给仓库分配管理员。
(3)更新异常
如果仓库换了管理员,则表中所有行的管理员ID都要改。
把仓库管理关系表分为二个表:
仓库管理:StorehourseManage(仓库ID,管理员ID) , 仓库Storehourse(仓库ID,存储物品ID,数量)
这样设计的数据库表示符合BCNF范式的,消除了删除异常、插入异常和更新异常。
2.四种范式之间存在的关系如下:
1NF[消除非主属性对键的部分函数依赖]->2NF[消除非主属性对键的传递函数依赖]->3NF[消除主属性对键的传递函数依赖]->BCNF
3.此外还有4NF,5NF
4NF处理相互独立的多值情况,5NF处理相互依赖的多值情况。
规范化的过程就是在数据库表设计时移除数据冗余的过程。随着规范化的进行,数据冗余越来越少,但是数据库的效率也越来越低。这就要求我们在设计数据库的时候能结合实际应用的性能要求,规范到合适的范式,一般情况下,如果性能允许的话,都要求规范到第三范式。