数据库范式的定义

在设计数据库的时候,为了设计出合理的关系型数据库,就需要遵循一些规范要求,这些规范要求被成为数据库范式。 目前,关系型数据库主要有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。一般来说,数据库满足前三个范式就可以了。

第一范式(1NF)

定义

所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分。简而言之,第一范式就是无重复的域。

解释

第一范式是指数据库表中的每个字段都是不可分解的,即数据库表中的字段具有原子性。 例如,我们有个学生信息表,如下:

idnameageaddress
001张三26山东省青岛市XX县XX镇XX村XX号
002李四25河南省开封市XX县XX区XX号

这个表就违反了第一范式,因为表中的字段 地址(address) 可以重新拆分为 省(address_province) 市(address_city) 详细地址(address_detail)

idnameageaddress_provinceaddress_cityaddress_detail
001张三26山东省青岛市XX县XX镇XX村XX号
002李四25河南省开封市XX县XX区XX号

如上图所示的表遵循了第一范式,对学生的地址管理就方便了很多,也能提高数据库的性能。比如我们要查询青岛市的学生有谁的时候。

第二范式(2NF)

定义

第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。

解释

第二范式是指在第一范式的基础上,确保表中的每一列都依赖与主键,而不能只依赖与主键(联合主键)的一部分。 例如我们有个学生选课表如下:

学生ID(student_id)课程名称(course_name)学分(credit)成绩(score)
001计算机科学与技术1080
002计算机科学与技术1059
001软件工程1398
002软件工程1395

上表中,主键为 学生ID(student_id)课程名称(course_name) 组成的联合主键,成绩(score) 完全依赖与这个联合主键,但 学分(credit) 只依赖于 课程名称(course_name) 。所以违反了第二范式,产生了字段 学分(credit) 的数据冗余。

学生ID(student_id)课程ID(course_id)成绩(score)
00100180
00200159
00100298
00200295
课程ID(course_id)课程名称(course_name)学分(credit)
001计算机科学与技术10
002软件工程13

如上所示,将 课程名称(course_name)学分(credit) 拆分到单独的课程表中,在学生选课表中使用 学生ID(student_id)课程ID(course_id) 构成联合主键,这样就遵循了第二范式,消除了字段冗余。

第三范式

定义

第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。

解释

第三范式其实就是要消除传递依赖,确保每个字段都与主键直接相关。 例如我们在第一个例子的学生信息表中添加几个字段,如下:

idnameageaddressclass班主任(class_head_teacher)
001张三26山东省青岛市XX县XX镇XX村XX号101班李老师
002李四25河南省开封市XX县XX区XX号102班王老师
003王五24河南省开封市XX县XX区XX号101班李老师

其中 班主任(class_head_teacher) 依赖于 class 字段,其余字段则依赖于 id 字段,出现了传递依赖,并且 班主任(class_head_teacher) 产生了冗余。

idnameageaddressclass_id
001张三26山东省青岛市XX县XX镇XX村XX号001
002李四25河南省开封市XX县XX区XX号002
003王五24河南省开封市XX县XX区XX号001
idclass_name班主任(class_head_teacher)
001101班李老师
002102班王老师

将班级相关的内容抽取成单独的班级表,学生表中值存放 class_id 做为管理。这样就符合了第三范式,消除了冗余。