什么是三大范式
第一范式:当关系模式R的所有属性都不能在分解为更基本的数据单位时,称R是满足第一范式的,简记为1NF。满足第一范式是关系模式规范化的最低要
求,否则,将有很多基本操作在这样的关系模式中实现不了。
第二范式:如果关系模式R满足第一范式,并且R得所有非主属性都完全依赖于R的每一个候选关键属性,称R满足第二范式,简记为2NF。
第三范式:设R是一个满足第一范式条件的关系模式,X是R的任意属性集,如果X非传递依赖于R的任意一个候选关键字,称R满足第三范式,简记为3NF
理解三大范式
一、数据库第一范式:
数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。(保持数据的原子性)
数据原子性很好理解,就是表中的字段不可再分。符合数据库第一范式的表,每个字段表意明确,看个例子:
![6832d04300f9871c5955ae56d7a735ad.png](https://i-blog.csdnimg.cn/blog_migrate/e0f526abb6e70b83a0826df88e69e8f5.jpeg)
这是一张简单的员工信息表,其中有工号、姓名、电话三个字段。通过电话这个字段获得的信息有可能是家庭电话,或是工作地点的电话,或是手机,因此表达的信息并不明确,我们可以改成这样:
![2cce835eb7fd2ff294da6876dcda478b.png](https://i-blog.csdnimg.cn/blog_migrate/350e1dce9e0405d770a7f04312f89200.jpeg)
那这样改完以后,表中所表达的信息就非常明确了。
二、数据库第二范式
在满足第一范式的基础上,实体的每个非主键属性完全函数依赖于主键属性(消除部分依赖)
主键:凡是接触过数据库的人,肯定都会知道主键,主键明确标识了每条记录,一般是一个字段,也可以由两个或两个字段组成。
依赖:对于X的每个值,Y都有一个值与之对应,反过来则不一定不成立,这叫做X函数决定Y,Y函数依赖X(X往往是主键)。
还拿上面的那张表举来说,对于每个工号,都有一个姓名与之对应,即工号决定姓名,姓名依赖工号;但由于员工之间可能有重名,一个姓名可能对应多个工号,所以姓名不能决定工号。
部分依赖:当主键由两个或两个以上字段构成,而表中的某些信息通过主键的一个字段就能唯一确定,我们称这样的依赖关系为部分依赖,比如这个例子:
![037306788f2f3707ec8c5e2867730d36.png](https://i-blog.csdnimg.cn/blog_migrate/3a10b16b1b9102e407bfe1e3407f9f56.jpeg)
学生选课(学号,姓名,专业,课程号,课程名,成绩),该表中一个学生可以选多门课,一门课有多个学生。学号和课程号可以唯一确定一条记录,因此用学号和课程号做主键。
表中的姓名、专业通过主键中的学号就能唯一确定,而课程名通过课程号唯一确定,这就是部分依赖,这样的设计不符合第二范式。
不符合第二范式会带来哪些问题呢?
1、数据信息冗余,可见上表
2、增删改会出现问题,比如有一门《微机原理》没有人选,那么由于缺少学号(主键之一)那么这门课就不能出现在表里。
如何解决呢,我们可以用关系分解的方法消除部分依赖,将上表改成如下三张表:
![cfb1b728c2b920cd2cfdda7aade00e94.png](https://i-blog.csdnimg.cn/blog_migrate/aab41587625af00b5faff2623928973d.jpeg)
![acbcf06dfc0b7681fe1b22486c6dc9ea.png](https://i-blog.csdnimg.cn/blog_migrate/a8ce78b42c2e640dfcf9f52227a2261f.jpeg)
![c0cbeed310d0c6a7e8b7394462291d23.png](https://i-blog.csdnimg.cn/blog_migrate/9240d082d460ec44e9b6891a9ff2ae10.jpeg)
数据库第三范式
在满足第二范式的基础上,在实体中不存在非主键属性传递函数依赖于主键属性。(表中字段[非主键]不存在对主键的传递依赖)
传递依赖:A依赖于B,B依赖于C,就可以说A依赖C。看这样一张表:
![f8b239fa6deb9676ed722476218b3851.png](https://i-blog.csdnimg.cn/blog_migrate/f1860647a4fa2d2242da474d403d6a59.jpeg)
这张表中有如下决定关系: 学号-->姓名,性别,系号-->决定系名,宿舍号-->决定宿舍电话,也有 学号-->系名,学号-->宿舍电话。
在这样一张表中则存在着传递依赖。也就是系名依赖系号,系号依赖学号,那么间接的系名依赖学号,宿舍号、宿舍电话和学号之间也有同样的关系。这样设计表的同样会带来数据冗余,操作异常等问题。那么我们同样可以用关系分解的分解的方法来消除传递依赖,将这张表分成三张表:
![4502c58607e38c6b2d1059abccf7b0f5.png](https://i-blog.csdnimg.cn/blog_migrate/8b824959810a32753f471d18873cf794.jpeg)
![d5436e60a7f01cd5c567c7628da1238e.png](https://i-blog.csdnimg.cn/blog_migrate/e29bbd3ab077fb0c58076a3ebbf6a8d0.jpeg)
![fc9a370cb65c0e750107938ec61a0f52.png](https://i-blog.csdnimg.cn/blog_migrate/b0169f9c1cce3059dbaca1e923953b62.jpeg)
这就是数据可设计的三范式了,在设计数据表的过程中注意三范式的应用,多多实践,有助于对三范式有更深入的理解。
冗余
冗余定义:是指在一个数据集合中重复的数据称为数据冗余。
好处
一定的冗余可以提高开发的效率
一定的冗余可以提升性能
场景和方式
1.空间换时间
有一张字典表 city 其中有 id 和 cityName 两个字段,有一张业务表,其中有 id 、cityId、XXX、XXX…字段。
如果查询业务表的话,就必须 join 一下 city 字典表,如果业务表很大很大,那么就会查询的很慢,这个时候我们就可以使用冗余来解决这个问题,直接将业务表中的 cityId 更换成 cityName,这样我们在查询业务表的时候就不需要去 join 那一张 city 的字典表了。这样的方式显然是不符合我们数据库设计的范式的,但是这样的冗余或许很有必要。
2.查询某一个状态值数据
业务表中有一个字段 status 用来存储提交和未提交,假设这张表中未提交的数据相对于提交的数据是很少的,当用户查询所有未提交的数据的时候,就需要在全部的数据,然后筛选出未同意的数据。如果这张业务表非常的庞大,那么这样的查询的效率就非常的慢。
这个时候我们就可以把这张业务表中的未同意的数据冗余到一张新表中,这样用户查询未提交的数据的时候就可以直接在这张未提交的表中查询,查询速度提交很多。
3.拆分活跃数据和不活跃数据
某业务表有这样的一个特点,用户往往都在查询最近三个月(或几个月的)数据,数据每天都在增长,由于数据库表的增长,查询变得原来越慢,性能遇到瓶颈。这个时候可以根据业务将这张表拆分成三个月内数据和三个月外数据,其中三个月外数据还可以按照年份(或月份或季度)拆分成不同的分片。这样用户的查询会大量的命中与三个月内的那张表中,而这张表的数据是有限的,并且数据量并不会特别大,从而解决性能的瓶颈。
![c038696c5dd7982699e2d32b66992b4b.png](https://i-blog.csdnimg.cn/blog_migrate/f803fc133427fc11cf523867edef605f.jpeg)
4.汇总数据单独存放
某业务表存放着每天的交易数据,用户有的时候想查看某个季度,某一年份的交易总额。几个用户还好,当用户量增加,查询数增加,那么实时查询的方法已经满足不了要求,这个时候我们可以将这一类的汇总数据单独的存放到一张表中,通过在夜间用户量较少的时候计算出来,当用户再次查询的时候我们直接显示这张汇总表中的数据,而不通过查询交易数据表实时计算,这样可以大量的提高性能。
在刚刚工作的时候很多人有这样的心里:抱怨说公司数据库设计的一点都不好,大量的数据冗余,这么连第二范式都不符合呢?这里应该使用外键链接啊…现在才明白自己当时是多么的无知。最近一直在反思自己是不是太浮躁,在别人面前班门弄釜,或许真的应该“将肉扣在碗底吃”?好好钻研技术,该说的就说,不该说的坚决不说。
参考
参考:
https://www.cnblogs.com/manastudent/p/6629050.html
https://www.cnblogs.com/knowledgesea/p/3667395.html
https://www.cnblogs.com/gjhjoy/p/3527370.html