在上学校数据库的课程时,总是觉得太抽象,或者用不到。直到自己做了个小项目,涉及到了数据库表的设计时,才发现这些东西的重要性。因此重新回顾了下数据库的范式,自己懂不算懂,要能整理好讲出来让别人也能懂才算懂
一. 常用概念(术语)
元组:数据库表中的一行就是一个元组
分量:元组的某个属性
键:表中可以唯一确定一个元组的某个属性(或属性组),键是元组的分量
候选键:如果表中可以唯一确定一个元组的属性或属性组不唯一,则这些都是候选键
主键:从候选键中选出来最有代表性的大哥就是主键
全键:如果一个键包含了所有属性,则这个键就是全键
主属性:在候选键中出现过的属性就是主属性
非主属性:除了主属性之外的属性就是非主属性,即没有在候选键中出现过
外键:在本表中不是键,但是在其他表中是键
函数依赖:A->B,称为B依赖与B,或者A决定B。体现在表中就是通过某个属性(或属性组)A可以推导出其他的属性(或属性组)B。例如(学号,课程号)->(学生姓名,年级,课程名称,成绩)
将这个A看成是键,B看成是非主属性
部分函数依赖:上述函数依赖的A,如果某个真子集就已经足够决定B,则A->B就是部分函数依赖。
例如:(学号,课程号)中的(课程号)->(课程名称),只用了A的一个真子集(课程号)就能推导出B(课程名称)。那么:
(学号,课程号)->(课程名称)就是部分函数依赖
完全函数依赖:不是部分函数依赖就是完全函数依赖,即(学号,课程号)->(成绩),这个(成绩)必须由键A的全部来推导,A就是最小不可分的。则称(学号,课程号)->(成绩)是完全函数依赖
上述的术语看起来总是懵懵的,每次都如初见,所以一定要有自己的理解方式,每次都去理解,加深印象
二.范式
一共有六个范式,回顾到BC范式
先举个例子,假设有这么一张经典的表:
(学号,学生名,课程号,课程名,成绩,学院,院长)
表中的键:
(学号,课程号)->(学生名,课程名,成绩,学院,院长)
通过(学号,课程号)这一属性组,可以推出其他所有的属性,所以这一属性组就是键
1.第一范式(1NF)
要求:属性不可分。
表的一个属性不能再继续往下分,例如表中的“学生名”,不能再分为“现用名”和“曾用名”。
2.第二范式(2NF)
要求:每个非主属性都完全依赖于键。
在上面的表中,非主属性有:(学生名,课程名,成绩,学院,院长),键:(学号,课程号)
这其中的函数依赖有:
1⃣️(学号)->(学生名,学院)
2⃣️(课程号)->(课程名)
3⃣️(学号,课程号)->(成绩)
4⃣️(学院)->(院长)
上述的函数依赖中,满足第二范式:非主属性完全依赖于键的只有(学号,课程号)->(成绩)
如果要满足所有非主属性都要饭完全依赖于键,此时这一张表是不够的,需要将这一张表拆分:
表1:
(学号,学生名,学院,院长)
其中:(学号)->(学生名,学院,院长)
表2:
(学号,课程号,成绩)
其中:(学号,课程号)->(成绩)
表3:
(课程号,课程名)
其中:(课程号)->(课程名)
这下每张表就都满足了非主属性完全依赖于键啦,也就是满足了第二范式。
3.第三范式(3NF)
要求:非主属性不传递函数依赖于键
乍一看不知道在说什么,再一看还是不知道,但是看看具体例子就明白了。
其实第二范式的表1中就存在这种关系:学号->学院->院长
其实就是非主属性出现了函数传递,如果要消除这种函数函数传递,则要将这张表继续拆分:
表1-1
(学号,学生名,学院)
表1-2
(学院,院长)
这样就满足了第三范式
4.BD范式(BCNF)
要求:在满足第三范式的基础上,主属性之间也不出现函数依赖关系(注意是主属性,而不是主键中属性,不一样)
即键内的属性不能相互推导,上面的例子无法说明这个问题,假设有一张图书管理的表(书架id,图书id,管理员id,图书数量)
一个管理员对应一个书架,书架上有不同的图书,每种图书有不同的数量
表中关系:
(书架id,图书id)->(管理员id,图书数量)
(管理员id,图书id)->(书架id,图书数量)
这里的主属性有:(书架id,管理员id,图书id)
而主属性间出现了依赖关系:(书架id)->(管理员Id),(管理员id)->(书架id)
不满足BC范式,会出现下述问题:
更新异常:如果更换管理员,则所有的行中的管理员id的字段都要换,开销大
删除异常:如果书架被清空,在清空图书id和图书数量的同时,则管理员id和书架id也会被清空
插入异常:在表为空的时候(书架是空的),就不能给书架分配图书管理员
因此,最好的做法就是拆分这张表,将图书管理与书架分开:
表1:(书架id,管理员id)
表2:(书架id,图书id,图书数量)
这样就满足了BC范式。
在设计数据库表的时候,一般满足到第三范式或BC范式即可