数据库的范式(Normal Forms)
数据库的规范化(Normalization)是关系型数据库设计中的一个核心概念,旨在通过消除数据冗余和减少数据依赖来优化数据库表的结构。范式(Normal Forms,简称 NF)是衡量数据库设计规范化程度的级别。范式级别越高,数据冗余越少,数据一致性越好,但通常表的数量会增多,查询时可能需要更多的连接操作。
以下是数据库中常见的范式,从低到高(即从宽松到严格)进行介绍:
1. 第一范式(First Normal Form - 1NF)
定义: 表中的所有列都必须是原子性的(Atomic),即不可再分。每个字段只包含一个值,不包含重复的组或多个值。
要求:
表中不应有重复的行。
所有属性都是原子性的,即不可分解为更小的数据项。
示例(不符合 1NF): | 学生ID | 姓名 | 电话号码 | | :----- | :--- | :------------------- | | 1 | 张三 | 13812345678, 13987654321 |
电话号码 列包含了多个值,不符合原子性。
符合 1NF 的改进: | 学生ID | 姓名 | 电话号码 | | :----- | :--- | :------------- | | 1 | 张三 | 13812345678 | | 1 | 张三 | 13987654321 |
2. 第二范式(Second Normal Form - 2NF)
定义: 在满足 1NF 的基础上,表中的所有非主键属性都必须完全函数依赖于主键(Primary Key)。
要求: 消除部分函数依赖(Partial Dependency)。如果主键是复合主键(由多个列组成),那么任何非主键属性都不能只依赖于主键的一部分。
示例(不符合 2NF): 假设有一个 订单详情 表,主键为 富人数据库 (订单ID, 商品ID) | 订单ID | 商品ID | 商品名称 | 订单日期 | | :----- | :----- | :------- | :------- | | 1 | A001 | 电脑 | 2025-05-18 | | 1 | A002 | 键盘 | 2025-05-18 | | 2 | A001 | 电脑 | 2025-05-19 |
商品名称 只依赖于 商品ID (主键的一部分),而不是完整的复合主键 (订单ID, 商品ID)。订单日期 只依赖于 订单ID。这就是部分函数依赖。
符合 2NF 的改进: 拆分成两个表
订单 表(主键:订单ID): | 订单ID | 订单日期 | | :----- | :------- | | 1 | 2025-05-18 | | 2 | 2025-05-19 |
商品详情 表(主键:商品ID): | 商品ID | 商品名称 | | :----- | :------- | | A001 | 电脑 | | A002 | 键盘 |
订单商品 表(主键:(订单ID, 商品ID),订单ID 和 商品ID 为外键): | 订单ID | 商品ID | | :----- | :----- | | 1 | A001 | | 1 | A002 | | 2 | A001 |
3. 第三范式(Third Normal Form - 3NF)
定义: 在满足 2NF 的基础上,表中的所有非主键属性都不能传递函数依赖于主键。
要求: 消除传递函数依赖(Transitive Dependency)。即,任何非主键属性都不能依赖于另一个非主键属性。它们都应该直接依赖于主键。
示例(不符合 3NF): 假设有一个 员工 表,主键为 员工ID | 员工ID | 员工姓名 | 部门ID | 部门名称 | | :----- | :------- | :----- | :------- | | 101 | 王一 | D01 | 销售部 | | 102 | 李二 | D02 | 技术部 |
部门名称 依赖于 部门ID,而 部门ID 是一个非主键属性。部门名称 间接依赖于 员工ID (通过 部门ID 传递)。这就是传递函数依赖。
符合 3NF 的改进: 拆分成两个表
员工 表(主键:员工ID): | 员工ID | 员工姓名 | 部门ID | | :----- | :------- | :----- | | 101 | 王一 | D01 | | 102 | 李二 | D02 |
部门 表(主键:部门ID): | 部门ID | 部门名称 | | :----- | :------- | | D01 | 销售部 | | D02 | 技术部 |
4. 巴斯-科德范式(Boyce-Codd Normal Form - BCNF / 3.5NF)
定义: 在满足 3NF 的基础上,对于表中每一个函数依赖 X -> Y(其中 X 决定 Y),X 都必须是**候选键(Candidate Key)**或包含候选键。
要求: 消除所有非主键属性对候选键的依赖以及候选键之间存在非平凡函数依赖的情况。BCNF 比 3NF 更严格,解决了当一个表有多个重叠的复合候选键时可能出现的冗余。如果一个关系模式只有一个候选键,或者有多个候选键但它们不重叠,那么它在 3NF 的基础上通常也满足 BCNF。
示例(不符合 BCNF 但符合 3NF): 假设一个表 学生课程导师,主键为 (学生ID, 课程),同时存在 (学生ID, 导师) 也是候选键,且 导师 决定 课程。 | 学生ID | 课程 | 导师 | | :----- | :--- | :--- | | 1 | 数据库 | A | | 1 | 网络 | B | | 2 | 数据库 | A |
这里 (学生ID, 课程) 是主键,导师 决定 课程 (导师 -> 课程),且 导师 不是候选键。这就不符合 BCNF。
符合 BCNF 的改进: 拆分成多个表
学生导师 表(主键:(学生ID, 导师)): | 学生ID | 导师 | | :----- | :--- | | 1 | A | | 1 | B | | 2 | A |
导师课程 表(主键:导师): | 导师 | 课程 | | :--- | :--- | | A | 数据库 | | B | 网络 |
5. 第四范式(Fourth Normal Form - 4NF)
定义: 在满足 BCNF 的基础上,消除多值依赖(Multi-valued Dependency - MVD)。
要求: 如果 A -> B 和 A -> C,且 B 和 C 之间没有直接依赖关系,那么它们应该被拆分到单独的表中。这主要处理一个实体可以有多个不相关属性集的情况。
6. 第五范式(Fifth Normal Form - 5NF / Project-Join Normal Form - PJNF)
定义: 在满足 4NF 的基础上,消除连接依赖(Join Dependency)。
要求: 无法进一步分解成更小的、无损连接的表。
总结与实践
在实际的数据库设计中:
达到 3NF 通常被认为是良好的实践,因为它在减少冗余和保证数据完整性之间取得了很好的平衡。大多数业务系统都以 3NF 作为设计目标。
BCNF 是更理想的目标,但通常只在遇到特定的重叠候选键问题时才需要深入考虑。
4NF 和 5NF 涉及更复杂的依赖关系,在大多数商业应用中不常用,因为它们通常会导致表的高度碎片化,增加查询的复杂性,并且收益递减。
在某些情况下,为了查询性能或简化应用开发,开发者会有意地进行反规范化(Denormalization),即在一定程度上引入数据冗余。但这需要在仔细权衡性能和数据一致性风险的基础上进行。