一、简介
我们数据库表设计的时候需要尽可能的遵循三范式,具体是
- 第一范式(1NF): 强调的是列的原子性,即列不能够再分成其他几列,也不能有重复的列
- 第二范式(2NF): 在第一范式的基础之上,要求所有 非主键字段 完全依赖 主键 , 不产生 部分依赖
- 第三范式(3NF): 在第二范式的基础之上,要求所有 非主键字段 直接依赖 主键 , 不产生 传递依赖
第一范式
我们看下面的员工表数据
姓名 | 性别 | 年龄 | 联系方式 | 家庭住址 |
---|---|---|---|---|
郝灵寒 | 男 | 28 | 13838381122 | 广东省深圳市龙华区民治街道 |
姜傲南 | 男 | 25 | 13838383344 | 广东省深圳市南山区白石洲东一坊 |
郭以南 | 男 | 23 | 13838385566 | 广东省深圳市福田区园岭街道 |
从严格的角度来说,明显不符合第一范式,因为家庭住址一栏还能拆分,比如省、市、区、街道,如下表。
姓名 | 性别 | 年龄 | 联系方式 | 省 | 市 | 区 | 详细地址 |
---|---|---|---|---|---|---|---|
郝灵寒 | 男 | 28 | 13838381122 | 广东省 | 深圳市 | 龙华区 | 民治街道 |
姜傲南 | 男 | 25 | 13838383344 | 广东省 | 深圳市 | 南山区 | 白石洲东一坊 |
郭以南 | 男 | 23 | 13838385566 | 广东省 | 深圳市 | 福田区 | 园岭街道 |
第二范式
- 第一范式的基础上
- 每一行数据都要有一个主键 (至少一个主键) 能唯一区分
- 非主键列全部依赖主键,没有产生 部分依赖
- 单个主键的表 都复合第二范式,第二范式主要是 对复合主键的表有效
我们先看下面一个表,首先他们是满足第一范式的,每一列都是不能再拆分的,学生编号和课程编号是组成了一个 复合主键 (由多个主键确定一组数据)。
学生编号 | 学生姓名 | 课程编号 | 课程名称 | 课程学分 |
---|---|---|---|---|
001 | 郝灵寒 | 11 | 篮球 | 5 |
002 | 姜傲南 | 13 | 足球 | 4 |
003 | 郭以南 | 11 | 篮球 | 5 |
003 | 郭以南 | 12 | 羽毛球 | 3 |
我们从上面可以知道,学生和课程之间的关系是多对多。学生的姓名依赖学生的编号,课程的名称和课程学分是依赖课程编号,这样就出现了部分依赖的问题,怎么解决呢?我们把它拆成 学生表 , 课程表 及 学生选课表 。
学生表
学生编号 | 学生姓名 |
---|---|
001 | 郝灵寒 |
002 | 姜傲南 |
003 | 郭以南 |
课程表
课程编号 | 课程名称 | 课程学分 |
---|---|---|
11 | 篮球 | 5 |
12 | 羽毛球 | 4 |
13 | 足球 | 3 |
学生选课表
学生编号 | 课程编号 |
---|---|
001 | 11 |
002 | 13 |
003 | 11 |
003 | 12 |
这样就满足了第二范式了
第三范式
什么是第三范式?
- 在第二范式的基础上
- 没有 传递依赖 (即不能出现某些字段依赖于主键之外的字段,而不依赖于主键)
传递依赖 :表中的某个字段不是直接依赖主键,而是通过某个 非主键字段 依赖,最终实现依赖主键。看下面的表:
学生编号 | 姓名 | 性别 | 年龄 | 班级编号 | 班级名称 |
---|---|---|---|---|---|
001 | 郝灵寒 | 男 | 28 | 201 | 二年级1班 |
002 | 姜傲南 | 男 | 25 | 202 | 二年级2班 |
003 | 郭以南 | 男 | 23 | 201 | 二年级1班 |
从上面我们知道学生和班级是多对一的关系,学生编号和班级编号并不是复合主键,所以满足第二范式( 第二范式是对复合主键生效 ),但是,学生编号可以决定班级编号,班级编号可以决定班级名称,从而出现了传递依赖。那怎么解决呢?我们要把班级表分出去,同时在学生中加上关联字段。
学生信息表
学生编号 | 姓名 | 性别 | 年龄 | 所属班级 |
---|---|---|---|---|
001 | 郝灵寒 | 男 | 28 | 201 |
002 | 姜傲南 | 男 | 25 | 202 |
003 | 郭以南 | 男 | 23 | 201 |
班级信息表
班级编号 | 班级名称 |
---|---|
201 | 二年级1班 |
202 | 二年级2班 |
这样就满足了第三范式了
结语
关于数据库的设计一般是尽量遵循三范式,但是实际中还得灵活处理
- 不遵循三范式,可能会有数据的冗余,但是有可能带来性能的提升
比如在一个大型的网站中,可能用户会有用户的信息,订单会有用户的编号,查询订单时需要显示同时显示用户名。如果说采用三范式,并且在用户和订单都很多的时候,甚至还需要连其他的表,那么数据上的连接会让查询更慢(还有一些甚至用户的数据和订单数据都不在一个数据库或者不在一个机房),而如果把用户名存在订单表了,那么就可以直接获取显示了,提示了性能(修改用户名时可以通过异步或者触发器方式处理订单的用户名)。但是如果是不展示用户名,而通过触发查看订单购买人(因为这个主键查询更快),就不需要存用户的名称而产生冗余数据了。实际中的三范式的设计的还是会根据业务需求及数据量去做一个衡量的。
- 如果流量小的平台尽量遵守三范式,基本对性能没有多大的影响