范式设计:数据表的范式有哪些,3NF指的是什么?
范式设计:数据表的范式有哪些,3NF指的是什么?
很多时候,当数据库运行一段时间后,我们才发现数据表设计的有问题。重新调整数据表的结构,就需要做数据迁移,还有可能影响程序的业务逻辑,以及网站的正常访问。所以在开始设置数据库的时候,我们就需要重视数据表的设计
准备知识
函数依赖
解释:∀ -->代表任意的 。
依赖:R(U)是 自变量集U上的关系集合 [函数集合]。 X,Y属于自变量集合,r是R(U)关系集合中的任意一个关系 [函数]。对于任意的t和s 属于r,若t[X] = s[X] 则 t[Y] = s[Y] ,那么称X函数决定Y,或者Y函数依赖于X,记作X–>Y也就是一个或者一组属性的值可以决定其他属性的值,候选键可以做到
部分函数依赖
设X,Y是关系R的两个属性集合,存在X->Y,若X’是X的真子集,存在X’->Y,则称Y部分依赖于X
例子:学生信息表R中(学号,身份证号,姓名),学号属性的取值是唯一的, 在R关系中,(学号,身份证号)->(姓名),(学号)->(姓名),(身份证号)->(姓名);所以姓名部分依赖于(学号,身份证号)
完全函数依赖
设X,Y是关系R的两个属性集合, X’是X的真子集,存在X->Y,但对于每一个X’ 都有X’!->Y,则称Y完全依赖于X
例子:学生信息表R(学生,班级,姓名),假设不同的班级学号有相同的,班级内学号不能相同,在R关系中,(学号,班级)->(姓名); 但是(学号)->(姓名)不成立,(班级)->(姓名)不成立,所以姓名完全依赖于(学号,班级)
传递函数依赖
设X,Y,Z是关系R中互不相同的属性集合,存在X->Y (Y!->X),Y->Z,则称Z传递函数依赖于X
例子:在关系R(学号,宿舍,费用)中,(学号)->(宿舍), (宿舍)!->学号, (宿舍) ->(费用) ,(费用)!->(宿舍) ,所以符合传递函数的要求
什么是范式
范式是数据表设计的基本原则,我们在设计关系型数据库模型的时候,需要对关系内部各个属性之间联系的合理化程度进行定义,这就有了不同等级的规范要求,这些规范要求被称为范式(NF)。可以吧范式理解为,一张数据表的设计结构需要满足某种设计标准的级别。
范式的级别
目前关系型数据库一共有6种范式,按照范式级别,从低到高分别是
- 1NF(第一范式)
- 2NF(第二范式)
- 3NF(第三范式)
- BCNF(巴斯 - 科德范式)
- 4NF(第四范式)
- 5NF(第五范式,又叫做完美范式)
数据库的范式设计越高阶,冗余度就越低。高阶范式一定符合低阶范式的要求
一般来说,数据表的设计应尽量满足3NF。但不绝对,有时候为了提高某种查询性能,我们还需要破坏范式规则,也就是反规范化
范式定义的元素
范式的定义会使用到主键和候选键(因为主键和候选键可以唯一标识元组[元组就是数据
])。
数据库中的键(Key)由一个或者多个属性组成。通常,我们也将候选键称为码,把主键也称为主码
例如 一个表有(id,name,sex,age)字段
- 超键:能唯一标识元组的属性集叫做超键 [
唯一标识数据,比如(id,name,sex,age)是超键,(id,name,sex)是超键,可以理解为所有能有效区分数据记录的属性(列)的集合,可以包含一些对区分数据记录无用的属性集,超键,超的主要意义是多于
] - 候选键:如果超键不包含多余属性,那么这个超键就是候选键 [
唯一标识数据,不包含多于属性的超键称为候选键,也就是在候选键中,若要再删除属性,就不能唯一标识元组了
] - 主键:用户可以从候选键中选择一个作为主键[
主键可以是单一字段也可以是字段集合
] - 外键:如果数据表R1中的某属性集不是R1的主键,而是另外一个数据表R2的主键,那么这个属性集就是数据表R1的外键
因为键可能是由多个属性组成的,针对单个属性,可以用主属性和非主属性 - 主属性:包含在任一候选键的属性称为主属性
- 非主属性:与主属性相对,指的是不包含在任何一个候选键中的属性
例子
球员表(player)
球员编号 | 姓名 | 身份证号 | 球队编号 |
---|---|---|---|
#123 | 张珊 | 14233219942381249 | #1 |
球队表(team)
球队编号 | 主教练 | 球队所在地 |
---|---|---|
#1 | 王五 | 北京 |
对于球员表
超键:球员编号或者身份证编号的任意组合 (球员编号)、(球员编号,姓名)、(身份证号,年龄)
候选键:是最小的超键 (球员编号)、(身份证号)
主键:就是从候选键中选择一个 (球员编号)
外键:(球员编号)
主属性:(球员编号)、(身份证号)
其他属性:(姓名)、(年龄)、(球队编号)
从1NF到3NF
1NF
1NF指的是数据库表中的任何属性都是原子性的,不可再分
理解:我们在设计某个字段的时候,对于字段X来说,就不能把字段X拆分成字段X-1和字段X-2。
2NF
2NF指的是数据表里面的非主属性都要和这个数据表的候选键[所有的候选键]有完全依赖的关系。
理解:比如一张球员比赛表,字段 球员编号、姓名、年龄、比赛编号、比赛时间、比赛场地等属性。
候选键和主键为(球员编号,比赛编号),我们可以通过候选键来决定如下的关系,(球员编号,比赛编号)–>(姓名、年龄、比赛时间、比赛场地、得分)
但是这个不满足第二范式,因为字段之间还存在如下关系:
(球员编号)—>(姓名,年龄)
(比赛编号)—>(比赛时间,比赛场地)
也就是说候选键中某个字段决定了非主属性。这样会产生以下问题
- 数据冗余:如果一个球员可以参加m场比赛没那么球员的姓名和年龄就重复了m-1次。一个比赛也可能会有n个球员参加,比赛的时间和地点就重复了n-1次
- 插入异常:如果想要添加一场新的比赛,但是没有确定参加球员都有谁,那么就没法插入
- 删除异常:如果我们要删除某个球员编号,如果没有单独保存比赛表的话,就会同时把比赛信息删除掉
- 更新异常:如果我们调整了某个比赛的时间,那么数据表中所有这个比赛的时间都需要调整,否则就会出现一场比赛时间不同的情况
某种程度上,2NF是对1NF原子性的升级,1NF告诉我们字段属性需要是原子性的,而2NF告诉我们一张表就是一个独立的对象
3NF
3NF在满足2NF的同时,对任何非主属性都不传递依赖于候选键
也就是不存在非主属性A依赖于非主属性B,非主属性B依赖于候选键的情况
理解:比如球员表, 表字段(球员编号,姓名,球队名称,球队主教练)
可以看出球员编号决定了球队名称。同时球队名称决定了球队主教练,非主属性球队教练就会传递依赖于球员编号,因此不符合3NF的要求
如果要符合3NF,可以把数据表拆分成下面的, 球员表(球员编号,姓名,球队名称) 球队表(球队名称,球队主教练)
总结
1NF需要保证表中每个属性都保持原子性;2NF需要保证表中的非主属性与候选键完全依赖;3NF需要保证表中的非主属性与候选键不存在传递依赖
范式这是给出了设计的标准,实际上设计数据表的时候,未必要符合这些原则。一方面是因为这些范式本身存在一些问题,可能会带来插入,更新,删除等的异常情况。另一方面可能降低查询的效率,因为范式等级越高,设计出来的数据表就越多,进行数据查询的的时候就可能需要关联多张表,从而影响查询效率