创建时间:

最后修改:


# 关系型数据库范式

设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。

目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。

此文章修改整理自: https://www.zhihu.com/question/24696366/answer/29189700



## 01. 一些概念

### 范式

范式是“符合某一种级别的关系模式的集合,表示一个关系内部各属性之间的联系的合理化程度”。实际上你可以把它粗略地理解为**一张数据表的表结构所符合的某种设计标准的级别**。

### 关系 与 关系模式

“关系”和“关系模式”的区别,类似于面向对象程序设计中”类“与”对象“的区别。”关系“是”关系模式“的一个实例,你可以把”关系”理解为一张带数据的表,而“关系模式”是这张数据表的表结构。

### 元组

可以把“元组”理解为一张表中的每条记录,也就是每一行。

### 属性

关系里的列。

### 码

关系中的某个属性或者某几个属性的组合,用于区分每个元组。

### 主属性

包含在任何一个码中的属性称为主属性。

### 异常

![](https://att.sxyz.blog/image/bksov.jpg)

- **插入异常:**无法单独新添加一个系,因为每个系是伴随着学生而存在于这张表中的。
- **删除异常:**若将某个系中的全部学生记录都删掉,那么这个系也不复存在了。
- **修改异常:**假如李小明转系到法律系,那么为了保证数据库中数据的一致性,需要修改三条记录中系与系主任的数据。
- **数据冗余:**每一名学生的学号、姓名、系名、系主任这些数据重复多次。每个系与对应的系主任的数据也重复多次。


### 函数依赖

我们可以这么理解:**若在一张表中,在属性(或属性组)X的值确定的情况下,必定能确定属性Y的值,那么就可以说Y函数依赖于X,写作 X → Y**。也就是说,在数据表中,不存在任意两条记录,它们在X属性(或属性组)上的值相同,而在Y属性上的值不同。这也就是“函数依赖”名字的由来,类似于函数关系 y = f(x),在x的值确定的情况下,y的值一定是确定的。

### 部分函数依赖 

假如 Y 函数依赖于 X,但同时 Y 并不完全函数依赖于 X,那么我们就称 Y 部分函数依赖于 X。

### 完全函数依赖

在一张表中,若 X → Y,且对于 X 的任何一个真子集(假如属性组 X 包含超过一个属性的话),X → Y 不成立,那么我们称 Y 对于 X **完全函数依赖**。

### 传递函数依赖 

假如 Z 函数依赖于 Y,且 Y 函数依赖于 X (严格来说还有一个X 不包含于Y,且 Y 不函数依赖于Z的前提条件),那么我们就称 Z 函数传递依赖于 X 。




## 02. 1NF

定义: **符合1NF的关系中的每个属性都不可再分**

![](https://att.sxyz.blog/image/mhiu2.jpg)

上图中的表就不符合 1NF 的设计规范。

事实上,你在关系型数据库中创建的表,都一定满足 1NF 规范。类似于下面的表这样: 

![](https://att.sxyz.blog/image/9ok77.jpg)

在符合 1NF 的数据库设计中可能存在着 **异常** ,所以我们需要提高设计标准,去掉导致上述四种问题的因素,使其符合更高一级的范式(2NF),这就是所谓的“规范化”。



## 03. 2NF

定义: **2NF 在 1NF 的基础之上,消除了非主属性对于码的部分函数依赖**

![](https://att.sxyz.blog/image/bksov.jpg)

在上表中,学生信息与课程信息全混在了一张表中: 

这里的码是 **(学号,课名)**,用它来标识每个元组。

对于**(学号,课名) → 姓名**,有 **学号 → 姓名**,存在非主属性 **姓名 **对码**(学号,课名)**的部分函数依赖。
对于**(学号,课名) → 系名**,有 **学号 → 系名**,存在非主属性 **系名 **对码**(学号,课名)**的部分函数依赖。
对于**(学号,课名) → 系主任**,有 **学号 → 系主任**,存在非主属性 **系主任** 对码**(学号,课名)**的部分函数依赖。

所以存在非主属性对于码的部分函数依赖,最高只符合1NF的要求,不符合2NF的要求。

这时候需要对这张表进行拆分: 

![](https://att.sxyz.blog/image/7yied.jpg)



## 04. 3NF

定义: **3NF 在 2NF的基础之上,消除了非主属性对于码的传递函数依赖**

![](https://att.sxyz.blog/image/7yied.jpg)

对于上图中的 **选课** 表,主码为**(学号,课名)**,主属性为 **学号** 和 **课名**,非主属性只有一个,为 **分数**,不可能存在传递函数依赖,所以 **选课** 表的设计,符合 3NF 的要求。

对于上图中的 **学生** 表,主码为 **学号**,主属性为 **学号**,非主属性为 **姓名**、**系名** 和 **系主任**。因为 学号 → 系名,同时 系名 → 系主任,所以存在非主属性 **系主任** 对于码 **学号** 的传递函数依赖,所以 **学生** 表的设计,不符合 3NF 的要求。

为了让数据表设计达到 3NF,我们必须进一步进行模式分解为以下形式:
**选课**(学号,课名,分数)
**学生**(学号,姓名,系名)
**系**(系名,系主任)
![](https://att.sxyz.blog/image/wjzxv.jpg)



## 05. BCNF

若:

1. 某公司有若干个仓库;
2. 每个仓库只能有一名管理员,一名管理员只能在一个仓库中工作;
3. 一个仓库中可以存放多种物品,一种物品也可以存放在不同的仓库中。每种物品在每个仓库中都有对应的数量。

基于此关系模式的关系(具体的数据)可能如图所示:

![](https://att.sxyz.blog/image/yu3v5.jpg)



**函数依赖集**:仓库名 → 管理员,管理员 → 仓库名,(仓库名,物品名)→ 数量
**码**               :(管理员,物品名),(仓库名,物品名)
**主属性**       :仓库名、管理员、物品名
**非主属性**   :数量

∵ 此关系模式中不存在 **非主属性** 对 **码** 的 部分函数依赖 和 传递函数依赖。

∴ 此关系模式符合 3NF。



但是在某些特殊情况下,即使关系模式符合 3NF 的要求,仍然存在着插入异常,修改异常与删除异常等问题,仍然不是 "好" 的设计。

造成此问题的原因:**存在着 主属性 对于 码 的 部分函数依赖 与 传递函数依赖。在此例中就是存在主属性 仓库名 对于码的部分函数依赖。**

解决办法就是要在 3NF 的基础上消除 主属性 对于 码 的部分与传递函数依赖: 

**仓库**(仓库名,管理员)
**库存**(仓库名,物品名,数量)