在重构我的项目数据库之前,我想先理清楚数据库的范式问题。

为什么要规范化

单个关系的大型数据库可能会导致数据重复。这种数据重复可能会导致:

  • 使关系非常大。

  • 维护和更新数据并不容易,因为它涉及搜索相关的许多记录。

  • 磁盘空间和资源的浪费和利用率低下。

  • 错误和不一致的可能性增加。

因此,为了处理这些问题,我们应该分析冗余数据的关系并将其分解为更小、更简单、结构良好且满足所需属性的关系。规范化是将关系分解为具有较少属性的关系的过程。

范式的作用主要是为了消除这些异常现象。


什么是范式

数据库规范化,又称范式、标准化,是数据库设计的一系列原理和技术,以减少数据库中数据冗余,增进数据的一致性。

范式是评价数据库模式规范化程度从低到高主要有:1NF、2NF、3Nf、BCNF、4NF、5NF。

现在数据库设计最多满足 3NF,普遍认为范式过高,虽然具有对数据关系更好的约束性,但也导致数据关系表数量增加,从而导致数据库 IO 更易繁忙。过高的范式,其实相当于将原本由数据库实现的关系约束,转移到了更高一级的数据库管理程序中实现。


1NF

表的列的具有原子性,不可再分解, 第一范式(1NF)解决了列重复的问题。

考察下面的关系表:

student_id name dept dept_phone course course grade
1 张三 CS 67123467 英语 英语 80
2 李四 SE 67123468 软件工程 软件工程 75
2 李四 SE 67123468 英语 英语 70

出现了两个含义相同的列,不满足1NF。删除重复的列,得到下面的关系表:

student_id name dept dept_phone course grade
1 张三 CS 67123467 英语 80
2 李四 SE 67123468 软件工程 75
2 李四 SE 67123468 英语 70

满足了1NF。

不满足1NF的关系表是比较反直觉的,往往我们一开始设计的数据库就已经满足了1NF。


2NF

第二范式(2NF)解决了非主键列对主键的部分函数依赖问题。

考虑下面的关系表,其已经满足了1NF:

student_id(PK) name dept dept_phone course(PK) grade
1 张三 CS 67123467 英语 80
2 李四 SE 67123468 软件工程 75
2 李四 SE 67123468 英语 70

其主键似乎可以设置为(student_id,course)。但是如果这样的话,存在下面的依赖关系:

dept_phone→dept→student_id

这意味着dept_phone与dept列仅依赖student_id,但不依赖另一个主键course。所以这个关系表不满足2NF,需要进一步分解。

分解后可以如下:

student_id(PK) name dept dept_phone
1 张三 CS 67123467
2 李四 SE 67123468
2 李四 SE 67123468
student_id(PK) course(PK) grade
1 英语 80
2 软件工程 75
2 英语 70

3NF

第三范式(3NF)解决了传递函数依赖问题。

考虑下面的关系表:

student_id(PK) name dept dept_phone
1 张三 CS 67123467
2 李四 SE 67123468
2 李四 SE 67123468

如果dept与dept_phone是一一对应的关系,那么在关系表中同时记录两个值就是重复的。此时存在依赖关系dept_phone→dept→student_id,存在非主键的传递依赖。因此这个关系表不满足3NF,需要进一步分解。

分解后可以如下:

student_id(PK) name dept
1 张三 CS
2 李四 SE
2 李四 SE
dept(PK) dept_phone
CS 67123467
SE 67123468
SE 67123468

BCNF

属于修正的第三范式,是防止主键的某一列会依赖于主键的其他列。 如果3NF 消除了主属性对码的部分函数依赖和传递函数依赖,就被称为 BCNF。

4NF

非主属性不应该有多值。如果有多值就违反了第四范式。4NF 是限制关系模式的属性间不允许有非平凡且非函数依赖的多值依赖。

5NF

第五范式消除了 4NF 中的连接依赖,表必须可以分解为较小的表,除非那些表在逻辑上拥有与原始表相同的主键。


对于BCNF以上的范式,我并不打算深入研究。不仅因为其过于复杂,也因为上面提到的,它们可能导致性能问题,所以并不实用。