数据库设计中的范式(Normalization)是数据库规范化的规则,用于组织数据表的结构,以减少数据冗余和提高数据完整性。
第一范式 (1NF):每个列都不可以再拆分
定义
第一范式强调每个字段的值必须是原子值,即不可再分割的最小单位。换句话说,表中的每一列都必须是单一值,不能包含列表、数组或其他复杂数据结构。
要求
- 每个字段只能存储单一值。
- 表中的每一行和列的交叉点(即单元格)只能存储一个值。
举例
一个没有符合第一范式的表可能是这样的:
学生ID | 姓名 | 电话号码 |
1 | 张三 | 123456, 789012 |
2 | 李四 | 345678 |
问题:
- “电话号码”列存储了多个值(如
123456, 789012
),违反了第一范式。
改进后,将电话号码拆分到单独的行中,使其符合第一范式:
学生ID | 姓名 | 电话号码 |
1 | 张三 | 123456 |
1 | 张三 | 789012 |
2 | 李四 | 345678 |
第二范式 (2NF):非主键列完全依赖于主键,而不能依赖于主键的一部分
定义
第二范式要求表必须满足第一范式,同时每个非主键列必须对整个主键完全依赖,而不能仅依赖于主键的一部分。
适用场景
- 只有当主键是由多个字段组成的复合主键时,才可能违反第二范式。如果主键是单一字段,就自然满足第二范式。
举例
以下表未符合第二范式:
学生ID | 课程ID | 课程名称 | 成绩 |
1 | 101 | 数学 | 95 |
1 | 102 | 英语 | 88 |
2 | 101 | 数学 | 78 |
问题:
- 主键是
(学生ID, 课程ID)
的组合。 - “课程名称”只依赖于
课程ID
,而不是整个主键(学生ID, 课程ID)
。 - 因此,违反了第二范式,因为“课程名称”不完全依赖于主键,而是依赖于主键的一部分。
改进后,拆分成两个表:
课程表:
课程ID | 课程名称 |
101 | 数学 |
102 | 英语 |
成绩表:
学生ID | 课程ID | 成绩 |
1 | 101 | 95 |
1 | 102 | 88 |
2 | 101 | 78 |
这样,“课程名称”只依赖于 课程ID
,不再依赖于主键的一部分,符合第二范式。
第三范式 (3NF):非主键列只依赖于主键,不依赖于其他非主键
定义
第三范式要求表必须满足第二范式,同时每个非主键列必须直接依赖于主键,而不能依赖于其他非主键列(即不存在传递依赖)。
要求
- 非主键列不能通过其他非主键列间接依赖主键。
举例
以下表未符合第三范式:
学生ID | 班级ID | 班级名称 | 班主任 |
1 | 201 | 一班 | 张老师 |
2 | 201 | 一班 | 张老师 |
3 | 202 | 二班 | 李老师 |
问题:
- 主键是
学生ID
。 - “班级名称”和“班主任”依赖于
班级ID
,而班级ID
又依赖于主键学生ID
。 - 这就形成了“传递依赖”(
学生ID
→班级ID
→班级名称/班主任
),违反了第三范式。
改进后,拆分成两个表:
班级表:
班级ID | 班级名称 | 班主任 |
201 | 一班 | 张老师 |
202 | 二班 | 李老师 |
学生表:
学生ID | 班级ID |
1 | 201 |
2 | 201 |
3 | 202 |
这样,“班级名称”和“班主任”直接依赖于 班级ID
,而与 学生ID
无关,符合第三范式。
总结
- 第一范式 (1NF):确保每个字段都是原子值,不能再拆分。
- 第二范式 (2NF):确保非主键字段完全依赖整个主键,而不是依赖主键的一部分。
- 第三范式 (3NF):确保非主键字段只依赖于主键,而不依赖于其他非主键字段。