范式
一张数据表的表结构所符合的某种设计标准的级别,范式越高,数据表的安全性越高,但查询速度越慢。数据库范式分为:1NF,2NF,3NF,BCNF,4NF,5NF,6NF;一般的数据库到3NF就可以满足基本的设计要求。
范式 | 定义 |
---|---|
1NF | 关系中每个属性不可再分 |
2NF | 在第一范式的基础上消除非主属性对于任何一个候选码的部分函数依赖 |
3NF | 在第二范式的基础上消除非主属性对于任何一个候选码的传递函数依赖 |
BCNF | 在第三范式的基础上消除主属性对于任何一个候选码的部分函数依赖和传递函数依赖 |
4NF | skr而止,更高的范式在实际中很少用到,不做研究 |
1NF
第一范式强调表的原子性,即每个属性不可再分。
例如下面这种就不符合1NF:
姓名 | |
---|---|
年龄 | 性别 |
2NF
若某关系R属于第一范式,且每一个非主属性完全函数依赖于任何一个候选码,则关系R属于第二范式。在介绍2NF之前介绍一些基本概念,以下表为例:
学号 | 姓名 | 系名 | 系主任 | 课名 | 分数 |
---|---|---|---|---|---|
123 | 张三 | 数学系 | 李四 | 高等数学 | 68 |
123 | 张三 | 数学系 | 李四 | 复变函数 | 88 |
234 | 王五 | 物理系 | 赵六 | 大学物理 | 76 |
234 | 王五 | 物理系 | 赵六 | 力学分析 | 90 |
这个表是符合1NF范式的,但是不符合2NF范式,让我们看下这个表存在的问题。
- 每一名学生的学号、姓名、系名、系主任这些数据重复多次。每个系与对应的系主任的数据也重复多次 —— 数据冗余过大
- 假如学校新建了一个系,但是暂时还没有招收任何学生,那么是无法将系名与系主任的数据单独地添加到数据表中去的 —— 插入异常
- 假如将某个系中所有学生相关的记录都删除,那么所有系与系主任的数据也就随之消失了(一个系所有学生都没有了,并不表示这个系就没有了) —— 删除异常
- 假如张三要转系到物理系,那么为了保证数据库中数据的一致性,需要修改两条记录中系与系主任的数据 —— 修改复杂
码和属性
注意下文所有的码都是相对整个关系表而言的。
主码 ∈ 候选码 ∈ 超码 ∈ 码
- 码:码是可以确定一个元组的所有信息的属性名或属性名组
例如在 {a,b,c,d}中,假设知道 a 的值就能确定 b,c,d 的值;假设知道 c,d 的值就可以确定 a,b 的值;那么 {a}、{c,d} 就是码。并且 {a,b}, {a,c}, {a,d}, {a,b,c}, {a,b,c,d} 等也都是码,因为它们也可以确定一个元组的所有值,即使很多余。
- 候选码:候选码的真子集中不存在码,候选码可以有多个,可以理解成最小的码。
就上面的例子而言,{a} 是候选码,{c,d} 是候选码,因为它们的真子集中不存在码。
而诸如 {a,b} 并不是候选码,因为它的真子集中含有 {a},且 {a} 是码。
- 主码:主码就是主键的意思,主码可以是任意一个候选码
还是上面的例子,主码是候选码 {a}, {c,d} 中的其中一个。既可以是 {a}, 也可以是 {c,d}。
-
主属性:包含在任意一个候选码中的属性称为主属性,例如 a,c,d。
-
非主属性:不包含在任何一个候选码中的属性称为非主属性,例如 b。
-
超码:如果 Y 部分函数依赖(稍后介绍)于 X,则称 X 为超码。候选码是最小的超码(这句话看完后面再慢慢体会)。
-
全码:包含整个属性组的码称为全码。
-
外码:如果一个关系中的一个属性是另外一个关系中的主码则这个属性为当前关系的外码。
函数依赖
简单说就是 X 属性(或属性组)确定了,Y 也能确定,则说 Y 函数依赖于 X,写作 X → Y
- 完全函数依赖(F):在一张表中,若 X → Y,且对于 X 的任何一个真子集 X’(假如属性组 X 包含超过一个属性的话),X ’ → Y 不成立,那么我们称 Y 对于 X 完全函数依赖。
例如上表中的(学号) → (姓名)、(学号,课名) → (分数),就是完全函数依赖。
- 部分函数依赖(P):与完全函数依赖相对,假如 Y 函数依赖于 X,但同时 Y 并不完全函数依赖于 X,那么我们就称 Y 部分函数依赖于 X。
例如上表中的(学号,课名) → (姓名),因为(学号)为(学号,课名)的真子集,并且(学号) → (姓名),所以(学号,课名) → (姓名)为部分函数依赖。
- 传递函数依赖(T):假如 Z 函数依赖于 Y,且 Y 函数依赖于 X,那么我们就称 Z 传递函数依赖于 X。
例如上表中的(学号) → (系名)、(系名) → (系主任),那么(学号) → (系主任)就是传递函数依赖,即(系主任)传递函数依赖于(学号)。
介绍完函数依赖,我们可以借助函数依赖定义候选码,假设 K 为某表中的一个属性或属性组,若除 K 之外的所有属性都完全函数依赖于 K,那么我们称 K 为候选码。此时我们可以重新理解第二范式的定义:在第一范式的基础上消除非主属性对于任何一个候选码的部分函数依赖。
从1NF到2NF
上面的表符合第二范式吗?我们来分析一下。分四步走:
- 找出数据表中所有的候选码。
- 根据第一步所得到的候选码,找出所有的主属性。
- 数据表中,除去所有的主属性,剩下的就都是非主属性了。
- 查看是否存在非主属性对候选码的部分函数依赖。
step1: 暴力搜索法;查看所有每一单个属性,当它的值确定了,是否剩下的所有属性值都能确定。查看所有包含有两个属性的属性组,当它的值确定了,是否剩下的所有属性值都能确定……查看所有包含了六个属性,也就是所有属性的属性组,当它的值确定了,是否剩下的所有属性值都能确定。看起来很麻烦是吧,但是这里有一个诀窍,就是假如a是候选码,那么所有包含了a的属性组,如(a,b)、(a,c)、(a,b,c)等等,都不是候选码了(因为作为候选码的要求里有一个“完全函数依赖”)。所以我们找到了所有的候选码只有一组(学号,课名)。
step2: 主属性(学号,课名)。
step3: 非主属性(姓名,系名,系主任,分数)。
step4: 查看是否存在非主属性对候选码的部分函数依赖
- 对于(学号,课名) → (姓名),有(学号) → (姓名),存在非主属性(姓名)对候选码(学号,课名)的部分函数依赖;
- 对于(学号,课名) → (系名),有 (学号) → (系名),存在非主属性(系名)对候选码(学号,课名)的部分函数依赖;
- 对于(学号,课名) → (系主任),有(学号) → (系主任),存在非主属性(学号)对候选码(学号,课名)的部分函数依赖。
为了让表3符合2NF的要求,我们必须消除这些部分函数依赖,只有一个办法,就是将大数据表拆分成两个或者更多个更小的数据表,在拆分的过程中,要达到更高一级范式的要求,这个过程叫做“模式分解”。模式分解的方法不是唯一的,以下是其中一种方法:
选课(学号,课名,分数);学生(学号,姓名,系名,系主任)
学号 | 课名 | 分数 |
---|---|---|
123 | 高等数学 | 68 |
123 | 复变函数 | 88 |
234 | 大学物理 | 76 |
234 | 力学分析 | 90 |
学号 | 姓名 | 系名 | 系主任 |
---|---|---|---|
123 | 张三 | 数学系 | 李四 |
234 | 王五 | 物理系 | 赵六 |
我们先来判断一下选课表与学生表,是否符合了2NF的要求?
对于选课表,其候选码是(学号,课名),主属性是(学号,课名),非主属性是(分数),(学号)确定,并不能唯一确定(分数),(课名)确定,也不能唯一确定(分数),所以不存在非主属性分数对于候选码(学号,课名)的部分函数依赖,所以此表符合2NF的要求。
对于学生表,其候选码是(学号),主属性是(学号),非主属性是(姓名,系名,系主任),因为候选码只有一个属性,所以不可能存在非主属性对于候选码的部分函数依赖,所以此表符合2NF的要求。这里有一个基本的原理就是如果候选码是单个属性,那么一定不存在非主属性对于候选码的部分函数依赖。
现在我们来看一下,现在进行同样的操作,是否还存在着之前的那些问题?
- 学生的姓名、系名与系主任,不再像之前一样重复那么多次了。——数据冗余有改进
- 插入一个尚无学生的新系的信息。因为学生表的码是学号,不能为空,所以此操作不被允许。——插入异常无改进
- 删除某个系中所有的学生记录该系的信息仍然全部丢失。——删除异常无改进
- 张三转系到物理系只需要修改一次张三对应的系的信息即可。——修改复杂有改进
所以说,仅仅符合2NF的要求,很多情况下还是不够的,而出现问题的原因,在于学生表中仍然存在非主属性(系主任)对于候选码(学号)的传递函数依赖。为了能进一步解决这些问题,我们还需要将符合2NF要求的数据表改进为符合3NF的要求。
3NF
若某关系R属于3NF,则每一个非主属性既不部分函数依赖于任何一个候选码,也不传递依赖于任何一个候选码。即3NF在2NF的基础之上,消除了非主属性对于候选码的传递函数依赖。
从2NF到3NF
由于之前四个步骤我们已经执行过了,现在我们直接找是否存在非主属性对候选码的传递函数依赖。
- 对于选课表,主码为(学号,课名),主属性为(学号,课名),非主属性只有一个(分数),不可能存在传递函数依赖,所以选课表的设计,符合3NF的要求。
- 对于学生表,主码为(学号),主属性为(学号),非主属性为(姓名,系名,系主任)。因为(学号) → (系名),同时(系名) → (系主任),所以存在非主属性(系主任)对于候选码(学号)的传递函数依赖,所以学生表的设计,不符合3NF的要求。
为了让数据表设计达到3NF,我们必须进一步进行模式分解为以下形式:
选课(学号,课名,分数);
学生(学号,姓名,系名);
系(系名,系主任)。
学号 | 课名 | 分数 |
---|---|---|
123 | 高等数学 | 68 |
123 | 复变函数 | 88 |
234 | 大学物理 | 76 |
234 | 力学分析 | 90 |
学号 | 姓名 | 系名 |
---|---|---|
123 | 张三 | 数学系 |
234 | 王五 | 物理系 |
系名 | 系主任 |
---|---|
数学系 | 李四 |
物理系 | 赵六 |
- 对于选课表,符合3NF的要求,之前已经分析过了。
- 对于学生表,候选码为(学号),主属性为(学号),非主属性为(姓名,系名),不存在非主属性对于候选码的传递函数依赖,所以符合3NF的要求。
- 对于系表,候选码为(系名),主属性为(系名),非主属性为(系主任),不可能存在非主属性对于候选码的传递函数依赖(至少要有三个属性才可能存在传递函数依赖关系),所以符合3NF的要求。
现在我们再来看一下,对于2NF存在的问题还存在吗?
- 数据冗余更加少了。——数据冗余有改进
- 插入一个尚无学生的新系的信息,因为系表与学生表目前是独立的两张表,所以不影响。——插入异常有改进
- 删除某个系中所有的学生记录,该系的信息不会丢失。——删除异常有改进
- 张三转系到物理系只需要修改一次张三对应的系的信息即可。——修改复杂有改进
由此可见,符合3NF要求的数据库设计,基本上解决了数据冗余过大、插入异常、删除异常、修改复杂的问题。
BCNF
若某关系R属于BCNF,则每一个属性既不部分函数依赖于任何一个候选码,也不传递依赖于任何一个候选码。即BCNF在3NF的基础之上,消除了主属性对于候选码的部分函数依赖和传递函数依赖。
让我们看一个经典的例子:
仓库名 | 管理员 | 物品名 | 数量 |
---|---|---|---|
上海仓 | 张三 | iPhone 11 Pro | 100 |
上海仓 | 张三 | iPhone 11 | 200 |
北京仓 | 李四 | iPhone 11 | 100 |
北京仓 | 李四 | iPad Mini 4 | 50 |
某公司有若干个仓库;
每个仓库只能有一名管理员,一名管理员只能在一个仓库中工作;
一个仓库中可以存放多种物品,一种物品也可以存放在不同的仓库中。每种物品在每个仓库中都有对应的数量。
那么关系模式仓库(仓库名,管理员,物品名,数量) 属于哪一级范式?
答:已知函数依赖集:(仓库名) → (管理员),(管理员) → (仓库名),(仓库名,物品名)→ (数量);候选码:(管理员,物品名)、(仓库名,物品名);主属性:(仓库名,管理员,物品名);非主属性:(数量);因为不存在非主属性对候选码的部分函数依赖和传递函数依赖,所以此关系模式属于3NF。
既然此关系模式已经属于了3NF,那么这个关系模式是否存在问题呢?我们来看以下几种操作:
- 先新增加一个仓库,但尚未存放任何物品,是否可以为该仓库指派管理员?不可以,因为物品名也是主属性,根据实体完整性的要求,主属性不能为空——插入异常。
- 某仓库的物品被清空后,需要删除所有与这个仓库相关的物品存放记录,会带来什么问题?仓库本身与管理员的信息也被随之删除了——删除异常。
- 如果某仓库更换了管理员,会带来什么问题?这个仓库有几条物品存放记录,就要修改多少次管理员信息——修改复杂。
从这里我们可以得出结论,在某些特殊情况下,即使关系模式符合3NF的要求,仍然存在着插入异常、删除异常、修改复杂的问题,仍然不是“好”的设计。
造成此问题的原因:存在着主属性对于码的部分函数依赖与传递函数依赖。(在此例中就是存在主属性(仓库名)对于候选码(管理员,物品名)或者(管理员)对于候选码(仓库名,物品名)的部分函数依赖。解决办法就是要在3NF的基础上消除主属性对于候选码的部分函数依赖与传递函数依赖。
考虑作如下拆分:
仓库(仓库名,管理员);
库存(仓库名,物品名,数量)
仓库名 | 管理员 |
---|---|
上海仓 | 张三 |
北京仓 | 李四 |
仓库名 | 物品名 | 数量 |
---|---|---|
上海仓 | iPhone 11 Pro | 100 |
上海仓 | iPhone 11 | 200 |
北京仓 | iPhone 11 | 100 |
北京仓 | iPad Mini 4 | 50 |
这样,之前的插入异常、删除异常、修改复杂的问题就被解决了。
参考:
- 王珊, 萨师煊. 数据库系统概论[M]. 第5版. 北京: 高等教育出版, 2014.
- 知乎作者:刘慰,链接:https://zhuanlan.zhihu.com/p/20028672.
- CSDN:数据库中码、候选码、主码的区别.