数据库范式的定义
在设计数据库的时候,为了设计出合理的关系型数据库,就需要遵循一些规范要求,这些规范要求被成为数据库范式。 目前,关系型数据库主要有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。一般来说,数据库满足前三个范式就可以了。
第一范式(1NF)
定义
所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分。简而言之,第一范式就是无重复的域。
解释
第一范式是指数据库表中的每个字段都是不可分解的,即数据库表中的字段具有原子性。 例如,我们有个学生信息表,如下:
id | name | age | address |
---|---|---|---|
001 | 张三 | 26 | 山东省青岛市XX县XX镇XX村XX号 |
002 | 李四 | 25 | 河南省开封市XX县XX区XX号 |
这个表就违反了第一范式,因为表中的字段 地址(address)
可以重新拆分为 省(address_province)
市(address_city)
详细地址(address_detail)
。
id | name | age | address_province | address_city | address_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 | 计算机科学与技术 | 10 | 80 |
002 | 计算机科学与技术 | 10 | 59 |
001 | 软件工程 | 13 | 98 |
002 | 软件工程 | 13 | 95 |
上表中,主键为 学生ID(student_id)
和 课程名称(course_name)
组成的联合主键,成绩(score)
完全依赖与这个联合主键,但 学分(credit)
只依赖于 课程名称(course_name)
。所以违反了第二范式,产生了字段 学分(credit)
的数据冗余。
学生ID(student_id) | 课程ID(course_id) | 成绩(score) |
---|---|---|
001 | 001 | 80 |
002 | 001 | 59 |
001 | 002 | 98 |
002 | 002 | 95 |
课程ID(course_id) | 课程名称(course_name) | 学分(credit) |
---|---|---|
001 | 计算机科学与技术 | 10 |
002 | 软件工程 | 13 |
如上所示,将 课程名称(course_name)
和 学分(credit)
拆分到单独的课程表中,在学生选课表中使用 学生ID(student_id)
和 课程ID(course_id)
构成联合主键,这样就遵循了第二范式,消除了字段冗余。
第三范式
定义
第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。
解释
第三范式其实就是要消除传递依赖,确保每个字段都与主键直接相关。 例如我们在第一个例子的学生信息表中添加几个字段,如下:
id | name | age | address | class | 班主任(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)
产生了冗余。
id | name | age | address | class_id |
---|---|---|---|---|
001 | 张三 | 26 | 山东省青岛市XX县XX镇XX村XX号 | 001 |
002 | 李四 | 25 | 河南省开封市XX县XX区XX号 | 002 |
003 | 王五 | 24 | 河南省开封市XX县XX区XX号 | 001 |
id | class_name | 班主任(class_head_teacher) |
---|---|---|
001 | 101班 | 李老师 |
002 | 102班 | 王老师 |
将班级相关的内容抽取成单独的班级表,学生表中值存放 class_id
做为管理。这样就符合了第三范式,消除了冗余。