数据库三大范式设计详解
什么是范式
数据库范式(Normalization)是关系数据库设计中的一套理论规范,目的是减少数据冗余、避免更新异常、保证数据完整性。范式级别越高,表结构拆分得越细,数据的冗余度越低。
第一范式(1NF):列不可再分
核心要求:表中的每一列都是不可再分的最小原子单元。
违反 1NF 的例子
学生表(学号,姓名,联系方式)
学号 | 姓名 | 联系方式
1001 | 张三 | 13800138000, zhangsan@email.com
1002 | 李四 | 13900139000, lisi@email.com
“联系方式”列包含电话和邮箱两个独立信息,违反了 1NF。
符合 1NF 的修正
学生表(学号,姓名,电话,邮箱)
学号 | 姓名 | 电话 | 邮箱
1001 | 张三 | 13800138000 | zhangsan@email.com
1002 | 李四 | 13900139000 | lisi@email.com
每个单元格只存一个不可拆分的数据项。
注意:1NF 在实践中
MySQL 的 JSON 类型看似违反 1NF(一个列里存了多个字段),但这是因为 MySQL 将 JSON 当作一个原子数据类型来处理,应用层通过 JSON 函数访问内部字段,所以不视为违反 1NF。
第二范式(2NF):完全依赖主键
核心要求:满足 1NF 的基础上,表中的非主键列必须完全依赖于主键,不能只依赖主键的一部分(即消除部分依赖)。
违反 2NF 的例子
订单明细表(订单ID,商品ID,商品名称,数量,单价)
主键:(订单ID,商品ID)联合主键
问题:商品名称只依赖于商品ID,不依赖于(订单ID, 商品ID)联合主键
符合 2NF 的修正
订单明细表(订单ID,商品ID,数量,单价)
主键:(订单ID, 商品ID)
商品表(商品ID,商品名称,单价)
主键:商品ID
将依赖主键部分的字段拆分到新表,消除部分依赖。
2NF 的适用范围
- 只有联合主键的表才可能出现违反 2NF 的问题
- 单字段主键的表天然满足 2NF
第三范式(3NF):不依赖非主键列
核心要求:满足 2NF 的基础上,非主键列不能传递依赖于其他非主键列。
违反 3NF 的例子
员工表(员工ID,姓名,部门编号,部门名称,部门地址)
问题:部门名称和部门地址依赖于部门编号,而部门编号依赖于员工ID
→ 传递依赖:员工ID → 部门编号 → 部门名称
符合 3NF 的修正
员工表(员工ID,姓名,部门编号)
部门表(部门编号,部门名称,部门地址)
3NF 的本质
3NF 消除的是”非主键列之间的依赖关系”。如果 A 列和 B 列之间存在确定的映射关系,就应该拆分到另一个表中去。
范式总结表格
| 范式 | 核心要求 | 消除的问题 |
|---|---|---|
| 1NF | 列不可再分 | 列数据不原子 |
| 2NF | 完全依赖主键 | 部分依赖 |
| 3NF | 不依赖非主键 | 传递依赖 |
范式的实际意义
遵循三大范式带来的好处:
1. 减少数据冗余:同样数据只存一份,节省存储
2. 避免更新异常:如果”商品名称”只存一处,改名只需改一个地方
3. 避免插入异常:没有订单也能先录入商品信息
4. 避免删除异常:删除订单不会误删商品信息
范式的局限性
过度范式化也不是好事:
– 查询时 JOIN 过多,性能下降
– 某些场景下适量的冗余反而更好(这就是反范式化)
– 高并发读场景下,冗余可以减少 JOIN 次数
面试常问题
Q:数据库范式一定是越高越好吗?
A:不一定。实际项目中通常是 3NF + 少量反范式的折中方案。3NF 能保证数据的完整性,反范式则用于提升特定查询的性能。面试时不要一棍子打死说”必须 3NF”。
Q:设计表时怎么快速判断几范式?
A:看主键(唯一主键→至少 2NF),看有没有重复存储的冗余列→可能是违反 3NF 的传递依赖。
Q:BCNF 和 3NF 有什么不同?
A:BCNF(Boyce-Codd 范式)是 3NF 的加强版。当表中有多个候选键且这些键有重叠时,3NF 可能仍有冗余,BCNF 则要求每个决定因素都是候选键。实际工程中极少用到 BCNF。


暂无评论内容