何为范式
顾名思义,规范的方式。数据库作为底层的存储系统,直接影响业务层的性能,因此,为了能够让开发人员科学规范地使用数据库,三大范式应运而生。本文将以较为简洁的文字并举例描述三大范式。
第一范式(1NF)
第一范式是指关系表R中的每列都是原子不可分的项,即每个属性都是最基本的数据项。这里用代码举个栗子:
// 员工类
typedef Employee struct {
Id string // 员工id
Name string // 员工姓名
Age int // 员工名字
Dept Department // 员工所属部门
}
// 部门类
typedef Department struct {
Id string // 部门id
Name string // 部门名字
Detail string // 部门详情
}
如果我们在数据库中建立一张员工表emp(id, name, age, dept),对于前三个属性都是基本类型,不可再分,而对于第四个属性dept,它在程序中其实对应于结构体,为复合属性,因此,按照第一范式正确的建表方式应该是将复合属性拆分为多个原子不可分的基本属性,即emp(id,name,age,dept_id,dept_name,dept_detail)。
第二范式(2NF)
第二范式是指在满足第一范式的情况下,关系表R中的所有非主属性都完全依赖于R的每一个候选关键属性。这句话怎么理解呢,这里还是举个栗子:
假如有一个学生课程表student_course(学号, 姓名, 课程名称, 成绩, 学分),关键字为组合关键字(学号, 课程名称),因为只有这两个属性一起才能决定一条记录,即(学号, 课程名称) → (姓名, 成绩, 学分) ,这个关系表便不符合第二范式,因为"姓名"仅依赖于"学号","学分"仅依赖于"课程名",因此,不满足第二范式条件。那么,不满足第二范式会有什么问题呢?如下:
- 数据冗余
对于一门课程,如果有n名学生选修,则这门课程的全部信息将会重复存储n-1次,同理,一个学生选修了m门课程,则学生全部信息会重复存储m-1次,导致数据冗余存储。 - 更新问题
如果要更新某门课程的学分,那么所有关联这门课程的记录都将更新,否则会出现数据不一致问题。 - 插入问题
假如新增一门课程,但是尚未有学生选修,则该门课程的信息无法入库。 - 删除问题
假如某门课程对应的记录完全被删除,则将导致这门课程的信息完全丢失。
那我们可以按照第二范式来改造上述关系表,将表才分成student(学号,姓名),cource(课程名称,学分),student_cource(学号, 课程名称, 成绩),这样便避免了上述问题。
第三范式(3NF)
第三范式是指,在满足第二范式的前提下,关系表R中的所有非主属性由主键直接决定,不存在间接依赖关系,简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。
如表emp(id,name,age,dept_id,dept_name,dept_detail),这张表中的员工id能够决定所有非主属性,单我们发现dept_name,dept_detail这两个非主属性也可以由非主属性dept_id决定,而dept_id又依赖于id,因此存在间接传递依赖,不满足第三范式。同时,可以看出,不满足第三范式的问题是存在大量的冗余数据,解决该问题的方式很简单,只需将原关系表拆分为emp(id,name,age, dept_id), dept(dept_id,dept_name, dept_detail),这样不管是对于表emp还是表dept,各自的非主属性都直接依赖于主键,满足第三范式,同时也解决了数据冗余的问题。
小结
数据库的三大范式是很基础同时也是非常重要的概念,深刻理解后,有利于我们在项目中合理地设计数据关系表,同时也能提高开发效率,降低存储成本等等。
网友评论