范式的理解
我们理解一个概念的时候,最好从它为什么会出现这个概念入手。
当我们设计关系数据库的时候,我们需要创建一张张的二维表,但是如果我们不正确的、随意地创建,很容易造成数据库的冗余,这时候就为了使得创建的关系数据库更为合适、正确,故制定了不同的规范来约束所创建的表的结构。这些不同的规范要求被称为不同的范式。
有六种范式(Database Normalization):第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)要满足后面的范式必须先满足前面的范式,也就是要满足第三范式,必须满足第二和第一范式。
我们往往只需要满足到第三范式便可以了。下面我们着重讲解前三个范式。
第一范式
所谓的第一范式便是满足表中的所有属性是不可再分的原子值。也就是表的每一列都是不可分割的。
反例:
姓名 | 网络账号 | 年龄 | |
QQ账号 | 微信账号 | ||
二狗 | 285841xxxx | vx123xxx | 20 |
以上的表中,网络账号这个属性分为了QQ账号和微信账号,这就是相当于把网络账号这一列分割了,不满足第一范式。
正确的写法如下:
姓名 | QQ账号 | 微信账号 | 年龄 |
二狗 | 285841xxx | vx123xxx | 20 |
这时候二狗看到了,他说:我才不只有一个qq号呢,我有俩个!
于是他在上面的表格基础上加了一个qq号码。
姓名 | QQ账号 | 微信账号 | 年龄 |
二狗 | 285841xxx 275741xxx | vx123xxx | 20 |
那么这样子操作是否正确呢?答案是不正确。为什么呢?因为在关系数据库中,每一个属性的值也是不可以分割的。也就是表的一个格子里不能填多个值。
正确的应该这样子:
姓名 | QQ账号 | 微信账号 | 年龄 |
二狗 | 285841xxx | vx123xxx | 20 |
二狗 | 275741xxx | vx123xxx | 20 |
注意:一般的关系模式中必须要满足第一范式,也就是说第一范式是其最低的要求!
第二范式
在第一范式的基础上,非主属性必须完全依赖于候选码(也就是在1NF的基础上消除了非主属性对码的部分函数依赖)
解释:上面涉及到几个概念,非主属性、候选码、部分函数依赖、主码。
下面简单介绍一下这些概念,具体的详解在另外我的几篇文章之中。
首先候选码其实就是能够唯一标识一个元组(二维表的行)的属性或者属性组。
比如说:
学号 | 姓名 | 兴趣 |
001 | 小美 | 唱歌 |
002 | 小丽 | 跳舞 |
学号这个属性可以唯一的标识一个元组,所有他就是候选码(键)。因为学号不会重复且能唯一标识一个元组,而姓名和兴趣可以有重复的情况。
除了学号,剩下的姓名、兴趣这俩个属性就是非主属性,因为这个表的候选码只有一个(一张表可以有多个候选码),那么就把这个候选码作为主键(码)(主键是从候选码中选出来的)。
注意:主键在一张表是不允许重复!!即如果规定了学号为主键那么学号这列的值就不许有重复的出现。
再来一个例子说明部分函数依赖:
学号 | 姓名 | 科目 | 个人成绩 |
001 | 小美 | 英语 | 90 |
002 | 小丽 | 数学 | 92 |
大家想一下,上面这个表的的候选键是什么呢?是学号吗?答案是否定的。因为,我们如果想知道小美的数学成绩的话,单单一个学号是不行的,因为每个学生学习了多个科目,单知道一个学号是不能唯一标识出某一科的个人成绩的。
因为只有一个候选键,所以该候选键为主键,所以上面的表的主键为组合主键:(学号,科目),非主属性就是成绩和姓名。
那我们看看什么是完全函数依赖(有关函数依赖详解可以看我上篇文章《快速理解函数依赖》)
在(学号,科目)→成绩 这个函数依赖关系中,我们要想推导出个人的成绩的话,只知道学号或者是科目是无法得到某人的成绩的,必须是由(学号,科目)整体来得到某个学生的成绩。这就是完全函数依赖。
注意:如果候选键都是只有一个属性,而非属性组,那么该表必定是满足第二范式的。因为候选码都是单个属性不存在真子集,自己就是一个整体,所以可以说所有非主属性完全依赖于候选键。
那什么是部分函数依赖呢?在一个表中,我们可以通过主键来获取其他非主属性的值,所以存在依赖关系为:(学号,科目)→姓名,但是我们观察这个依赖关系,我们要得到姓名这个属性,只需要这个主键中的一个属性:学号就够了,因为一个学号对应一个学生姓名,也就是说通过主键的真子集—学号就可以推导出非主属性—姓名的值,其存在依赖关系:学号→姓名。这就是属于部分函数依赖。
这时候细心的小伙伴会发现,在成绩表中非主属性姓名部分依赖于主键(学号,科目),所以上表不符合第二范式,为了满足第二范式我们应该把姓名这一列删除(因为前面已经有了存放姓名的表—学生表,所以可以直接删除,如果没有存放姓名的表,那么就需要新建一个)
所以正确的成绩表为:
学号 | 科目 | 个人成绩 |
001 | 英语 | 90 |
002 | 数学 | 92 |
001 | 数学 | 88 |
第三范式
定义:在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)
解释:也就是说,满足第三范式的表,其非主属性值只能由主属性推导出来。
上面这句话有个概念为传递依赖。什么意思呢?
举个例子:
学号 | 所在系 | 系主任 |
0012 | 电子商务 | xx老师 |
上面这个表中,主键为学号,非主属性为所在系、系主任。存在以下依赖关系,学号→所在系,所在系→系主任,这时候我们可以通过学号得到所在系,在通过所在系得到所在系的主任,所以:学号→系主任,这样的介绍传递依赖。
那么怎么改呢?很简单,其中的方法就是对表的分解,可以分为俩个表:
学号 | 所在系 |
0012 | 电子商务 |
学号 | 系主任 |
0012 | xx老师 |
最后再说一句:范式不是越高越好,太高了会造成数据库表过多,关系过复杂,适合即可。