第一范式 - 1NF
遵循原子性。即,表中字段的数据,不可以再拆分。先看一个不符合第一范式的表结构,如下:
员工编码 |
姓名 |
年龄 |
001 |
销售部小张 |
28 |
002 |
运营部小黄 |
25 |
003 |
技术部小高 |
22 |
在这一个表中的,姓名 字段下的数据是可以再进行拆分的,因此它不符合第一范式,那怎么样才符合第一范式呢?如下:
员工编码 |
部门 |
姓名 |
年龄 |
001 |
销售部 |
小张 |
28 |
002 |
运营部 |
小黄 |
25 |
003 |
技术部 |
小高 |
22 |
那是否遵循第一范式就一定是好的呢?如下:
员工编码 |
姓名 |
地址 |
001 |
小张 |
江西省南昌市东湖区 |
002 |
小黄 |
广东省佛山市禅城区 |
003 |
小高 |
湖北省武汉市新洲区 |
通过观察上述表结构,我们发现,地址是可以再进一步拆分的,比如:
员工编码 |
姓名 |
省 |
市 |
区 |
001 |
小张 |
江西省 |
南昌市 |
东湖区 |
002 |
小黄 |
广东省 |
佛山市 |
禅城区 |
003 |
小高 |
湖北省 |
武汉市 |
新洲区 |
虽然拆分后,看上去更符合第一范式了,但是如果项目就只需要我们输出一个完整地址呢?那明显是表在没拆分的时候会更好用。所以范式只是给了我们一个参考,我们更多的是要根据项目实际情况设计表结构。
第二范式 - 2NF
在满足第一范式的情况下,遵循唯一性,消除部分依赖。即,表中任意一个主键或任意一组联合主键,可以确定除该主键外的所有的非主键值。再通俗点讲就是,一个表只能描述一件事情。我们用一个经典案例进行解析。
学号 |
姓名 |
年龄 |
课程名称 |
成绩 |
学分 |
001 |
小张 |
28 |
语文 |
90 |
3 |
001 |
小张 |
28 |
数学 |
90 |
2 |
002 |
小黄 |
25 |
语文 |
90 |
3 |
002 |
小黄 |
25 |
语文 |
90 |
3 |
003 |
小高 |
22 |
数学 |
90 |
2 |
我们先分析一下表结构。
1. 假设学号是表中的唯一主键,那由学号就可以确定姓名和年龄了,但是却不能确定课程名称和成绩。
2. 假设课程名称是表中的唯一主键,那由课程名称就可以确定学分了,但是却不能确定姓名、年龄和成绩。
3. 虽然通过学号和课程名称的联合主键,可以确定除联合主键外的所有的非主键值,但是基于上述两个假设,也不符合第二范式的要求。
那我们应该如何调整表结构,让它能复合第二范式的要求呢?
我们可以基于上述的三种主键的可能,拆分成 3 张表,保证一张表只描述一件事情。
1. 学生表 - 学号做主键
学号 |
姓名 |
年龄 |
001 |
小张 |
28 |
002 |
小黄 |
25 |
003 |
小高 |
22 |
2. 课程表 - 课程名称做主键
课程名称 |
学分 |
语文 |
3 |
数学 |
2 |
3. 成绩表 - 学号和课程名称做联合主键
学号 |
课程名称 |
成绩 |
001 |
语文 |
90 |
001 |
数学 |
90 |
002 |
语文 |
90 |
002 |
语文 |
90 |
003 |
数学 |
90 |
这时候我们可能会想,为什么我们就要遵循第二范式呢?不遵循第二范式会造成什么样的后果呢?
1. 造成整表的数据冗余。
如学生表,可能我就只有2个学生,每个学生都有许多的信息,比如,年龄、性别、身高、住址......如果与课程信息放到同一张表中,可能每个学生有3门课程,那数据总条数就会变成6条了。但是通过拆分,学生表我们只需要存储 2 条学生信息,课程表只需要存储 3 条课程信息,成绩表就只需保留学号、课程名称和成绩字段。
2. 更新数据不方便。
假设,课程的学分发生了变更,那我们就需要把整表关于该课程的学分都要更新一次,但如果我们拆分出课程表,那我们就只需要把课程表中的课程信息更新就行。
3. 插入数据不方便或产生异常。
① 假设主键是学号或课程名称,我们新增了某个课程,需要把数据插入到表中,这时,可能只有部分人有选修这门课程,那我们插入数据的时候还要规定给哪些人插入对应的课程信息,同时可能由于成绩还没有,我们需要对成绩置空,后续有成绩后还得重新更新一遍。
② 假设主键是学号和课程名称的联合主键。同样也是新增了某课程,但是暂时没有人选修这门课,缺少了学号主键字段数据,会导致课程信息无法插入。
第三范式 - 3NF
在满足第二范式的情况下,消除传递依赖。即,在任一主键都可以确定所有非主键字段值的情况下,不能存在某非主键字段 A 可以获取 某非主键字段 B。
仍然用一个经典例子来解析
学号 |
姓名 |
班级 |
班主任 |
001 |
小黄 |
一年级(1)班 |
高老师 |
这个表中,学号是主键,它可以唯一确定姓名、班级、班主任,符合了第二范式,但是在非主键字段中,我们也可以通过班级推导出该班级的班主任,所以它是不符合第三范式的。
那怎么设计表结构,才是符合第三范式的呢?
1. 学生表
学号 |
姓名 |
班级 |
001 |
小黄 |
一年级(1)班 |
2. 班级表
班级 |
班主任 |
一年级(1)班 |
高老师 |
通过把班级与班主任的映射关系另外做成一张映射表,我们就成功地消除了表中的传递依赖了。
总结
不知道读者们有没有发现,以上所介绍的范式的最终目的都是为了减少我们的工作量呢?所以说,尽管范式是一种很好的指导规范,但在实际应用中,我们也不需要太局限在范式中,更多的是应该从项目中出发,设计出合理的表结构。
以下是本篇三范式的简单总结:
- 第一范式(1 NF):字段不可再拆分。
- 第二范式(2 NF):表中任意一个主键或任意一组联合主键,可以确定除该主键外的所有的非主键值。
- 第三范式(3 NF):在任一主键都可以确定所有非主键字段值的情况下,不能存在某非主键字段 A 可以获取 某非主键字段 B。