数据库的三范式是什么?
数据库三范式(Normal Form,NF)是 “关系型数据库设计的规范”,目的是 “减少数据冗余、避免更新异常(插入 / 删除 / 修改异常)、保证数据一致性”,三范式的要求逐级严格,具体定义如下:
# 1. 第一范式(1NF,First Normal Form):原子性
定义:表中的每一列(属性)必须是 “原子值”(不可再分的数据项),即列不能包含多个值或复合数据(如 “地址” 列不能同时存储 “省、市、区”,需拆分为 “省份”“城市”“区县” 三列);
示例(不满足 1NF):
用户 ID 姓名 联系方式(不满足原子性) 1 张三 13800138000,zhangsan@xxx.com (opens new window) 示例(满足 1NF):
用户 ID 姓名 手机号 邮箱 1 张三 13800138000 zhangsan@xxx.com 核心要求:列不可再分,确保数据的最小存储单元。
# 2. 第二范式(2NF,Second Normal Form):完全函数依赖
前提:满足第一范式;
定义:表中的每一列(非主键列)必须 “完全函数依赖” 于主键(或主键组合),即非主键列不能依赖于主键的一部分(避免 “部分函数依赖”);
关键概念:
- 函数依赖:若通过主键的值可唯一确定某列的值,称该列依赖于主键(如 “用户 ID” 可确定 “姓名”,即 “姓名” 依赖于 “用户 ID”);
- 部分函数依赖:若主键是组合主键(如 “订单 ID + 商品 ID”),非主键列仅依赖于组合主键的一部分(如 “商品名称” 仅依赖于 “商品 ID”,与 “订单 ID” 无关);
示例(不满足 2NF):
主键为 “订单 ID + 商品 ID” 的订单表:
| 订单 ID | 商品 ID | 订单日期(依赖订单 ID) | 商品名称(依赖商品 ID) |
|---|---|---|---|
| 1001 | 2001 | 2024-01-01 | 手机 |
问题:“订单日期” 依赖于 “订单 ID”(主键的一部分),“商品名称” 依赖于 “商品 ID”(主键的一部分),存在部分函数依赖;
示例(满足 2NF):
拆分订单表(主键:订单 ID):
| 订单 ID | 订单日期 | | ------- | ---------- | | 1001 | 2024-01-01 |
拆分订单项表(主键:订单 ID + 商品 ID):
| 订单 ID | 商品 ID | | ------- | ------- | | 1001 | 2001 |
拆分商品表(主键:商品 ID):
| 商品 ID | 商品名称 | | ------- | -------- | | 2001 | 手机 |
核心要求:非主键列必须依赖于完整的主键,避免部分依赖。
# 3. 第三范式(3NF,Third Normal Form):传递函数依赖
前提:满足第二范式;
定义:表中的每一列(非主键列)必须 “直接依赖” 于主键,不能 “传递依赖” 于主键(即非主键列不能依赖于其他非主键列);
关键概念:
- 传递函数依赖:若 A 依赖于主键,B 依赖于 A,则 B 传递依赖于主键(如 “用户 ID” 是主键,“部门 ID” 依赖于 “用户 ID”,“部门名称” 依赖于 “部门 ID”,则 “部门名称” 传递依赖于 “用户 ID”);
示例(不满足 3NF):
主键为 “用户 ID” 的用户表:
| 用户 ID | 姓名 | 部门 ID | 部门名称(依赖部门 ID) |
|---|---|---|---|
| 1 | 张三 | 3001 | 技术部 |
问题:“部门名称” 依赖于 “部门 ID”(非主键列),“部门 ID” 依赖于 “用户 ID”(主键),因此 “部门名称” 传递依赖于 “用户 ID”;
示例(满足 3NF):
拆分用户表(主键:用户 ID):
| 用户 ID | 姓名 | 部门 ID | | ------- | ---- | ------- | | 1 | 张三 | 3001 |
拆分部门表(主键:部门 ID):
| 部门 ID | 部门名称 | | ------- | -------- | | 3001 | 技术部 |
核心要求:非主键列之间不能存在依赖关系,避免传递依赖。
# 4. 三范式的核心目标与权衡
- 核心目标:减少数据冗余(如 “部门名称” 无需在用户表中重复存储)、避免更新异常(如修改 “部门名称” 时,仅需修改部门表,无需修改用户表);
- 权衡:过度规范化(如拆分过多表)会导致多表关联查询(如查询用户信息需关联用户表和部门表),增加查询复杂度和性能开销;因此,实际设计中可能 “反范式”(如为提升查询效率,在用户表中冗余 “部门名称”),需在 “冗余” 和 “性能” 之间平衡。