数据库的第一、二、三范式分别解决了什么问题?一文详解

目录

第一范式:列的原子性

1.核心口号

2.解决的问题

3.反面教材(违反1NF)

第二范式:消除部分依赖

1.核心口号

2.解决的问题

3.反面教材(违反2NF)

第三范式:消除传递依赖

1.核心口号

2.解决的问题

3.反面教材(违反3NF)

总结与对比

面试必背口诀


在数据库设计的面试中,"范式"是一个绕不开的高频考点。很多开发者在实际工作中可能习惯"一把梭",把所有字段塞进一张大表里,但到了面试现场或者处理复杂业务时,就会因为不懂范式而导致数据库设计冗余严重、维护困难。

简单来说,数据库范式就是一套"拆表规则"。它的核心目的只有一个:减少数据冗余,避免数据异常(插入、更新、删除异常)。

今天我们就通过具体的案例和表格,把第一、二、三范式彻底讲透。


第一范式:列的原子性

1.核心口号

"列不能再分。"

2.解决的问题

第一范式(1NF)是数据库设计的最基本要求。它要求表中的每一个字段都必须是不可分割的原子值。如果一个字段里存了"多个值",就不符合1NF。

3.反面教材(违反1NF)

假设我们有一张学生联系表:

学号 姓名 联系方式
001 张三 13800001234, 010-88888888
002 李四 13900005678

问题分析:

"联系方式"这一列里,张三既有手机号又有座机号,它们被逗号分隔存在同一个格子里。

  • 查询困难: 如果你想查"所有有座机号的学生",SQL写起来会非常麻烦。
  • 数据混乱: 你无法单独约束"手机号"必须是11位数字。

修正方案(符合1NF):

将"联系方式"拆分,或者将多值转为多行。

学号 姓名 手机号 座机号
001 张三 13800001234 010-88888888
002 李四 13900005678 (空)

总结: 只要你的表里出现了"一组数据"塞在一个格子里的情况,就违反了第一范式。


第二范式:消除部分依赖

1.核心口号

"不能只靠一半。"

2.解决的问题

第二范式(2NF)建立在1NF的基础上。它主要针对复合主键 (由两个或多个字段共同作为主键)的表。它要求:表中的非主键字段,必须完全依赖于整个主键,而不能只依赖主键的一部分。

3.反面教材(违反2NF)

假设我们有一张"学生选课成绩表"。

  • 一个学生可以选多门课。
  • 一门课也可以被多个学生选。
  • 所以,我们需要用**【学号 + 课程名】**这两个字段联合起来,才能唯一确定一条记录(比如张三的数学成绩)。
学号 (主键1) 课程名 (主键2) 成绩 学生姓名 所在系
001 数学 90 张三 计算机系
001 英语 85 张三 计算机系
002 数学 95 李四 外语系

问题分析:

我们来看看表里的非主键字段(成绩、学生姓名、所在系)对主键的依赖关系:

  • 成绩: 必须知道"谁"(学号)考了"哪门课"(课程名),才能确定成绩。这是完全依赖,没问题。
  • 学生姓名: 只要知道"学号"是001,就知道是"张三"。根本不需要知道"课程名"。这就是部分依赖(只依赖了主键的一半)。
  • 所在系: 同理,只要知道学号,就知道他在哪个系,跟选了什么课无关。这也是部分依赖

如果不拆表,后果很严重:

  • 数据冗余: 张三选了10门课,"张三"和"计算机系"这两个信息就要重复存储10次。
  • 更新异常: 如果张三转系了,或者改名了,你需要把这张表里所有属于张三的记录全部找出来修改。漏改一条,数据就不一致了。
  • 插入异常: 如果有一个新生(比如王五)刚入学,还没有选课(没有课程名),你就没法把他的名字存进这张表,因为主键的一部分(课程名)不能为空。

修正方案(符合2NF):

把只依赖"学号"的字段拆出去,建立新表。

表1:选课表(主键:学号+课程名)

学号 课程名 成绩
001 数学 90
001 英语 85

表2:学生表(主键:学号)

学号 学生姓名 所在系
001 张三 计算机系
002 李四 外语系

总结: 只要表里有复合主键,就要检查有没有字段是"偷懒"的,只依赖了主键的一部分。如果有,就把它拆出去。


第三范式:消除传递依赖

1.核心口号

"不能隔山打牛。"

2.解决的问题

第三范式(3NF)建立在2NF的基础上。它要求:表中的非主键字段,必须直接依赖于主键,而不能依赖于另一个非主键字段。即:A决定B,B决定C,那么A决定C就是传递依赖,C应该被拆分出去。

3.反面教材(违反3NF)

现在我们看上面的**"表2:学生表"** 。假设它的主键是学号

学号 (主键) 学生姓名 所在系 系主任
001 张三 计算机系 王教授
002 李四 计算机系 王教授
003 王五 外语系 赵教授

问题分析:

我们来分析依赖关系:

  • 学号 → 所在系: 学号决定了学生属于哪个系。
  • 所在系 → 系主任: 一个系通常只有一个系主任,所以"所在系"决定了"系主任"。
  • 结论: "系主任"是通过"所在系"间接依赖于"学号"的。这就是传递依赖

如果不拆表,后果很严重:

  • 数据冗余: 计算机系有1000个学生,"王教授"这个名字就要重复存1000次。
  • 更新异常: 如果王教授退休了,换成了李教授。你需要修改学生表里所有"计算机系"对应的1000条记录。
  • 插入异常: 如果学校刚成立了一个"人工智能系",还没有招学生(没有学号),你就没法把这个新系和它的系主任存进数据库(因为主键学号不能为空)。

修正方案(符合3NF):

把"系主任"这个字段拆出去,让"所在系"做主键。

表1:学生表(主键:学号)

学号 学生姓名 所在系 (外键)
001 张三 计算机系
002 李四 计算机系

表2:系部表(主键:所在系)

所在系 系主任
计算机系 王教授
外语系 赵教授

总结: 只要发现表里有字段A依赖字段B,而字段B又依赖主键,那就赶紧把A拆出去。不要让非主键字段之间存在依赖关系。


总结与对比

为了方便记忆,我们可以把这三个范式看作是一个层层递进的"拆表"过程:

范式 核心解决的问题 依赖类型 案例场景
1NF 字段不可分 一个格子里存了"手机,座机"。
2NF 消除部分依赖 非主键列只依赖主键的一部分 选课表(学号+课程):姓名只依赖学号。
3NF 消除传递依赖 非主键列依赖另一个非主键列 学生表(学号):系主任依赖所在系。

面试必背口诀

  • 1NF: 原子性,列不可分。
  • 2NF: 完全依赖,不依赖主键的一半(针对复合主键)。
  • 3NF: 直接依赖,不隔山打牛(消除传递依赖)。
    在实际开发中,一般数据库设计达到第三范式就足够了。当然,为了性能优化(比如减少表连接JOIN),有时候我们会适当进行"反范式化"设计,但这属于进阶话题,前提是你得先懂范式。

以上就是本篇文章的全部内容,喜欢的话可以留个免费的关注呦~~~

相关推荐
卢傢蕊3 小时前
MySQL全量、增量备份与恢复
数据库·mysql
码农垦荒笔记3 小时前
MySQL主从延迟根因诊断法:从现象到本质的全链路排查指南
数据库·mysql·主从复制
我不是8神3 小时前
CAP 定理与 etcd 核心知识点总结
数据库·etcd
kiku18183 小时前
Mysql故障排查与优化
数据库·mysql
刘~浪地球4 小时前
Redis 从入门到精通(二):数据类型详解
数据库·redis·缓存
小韩博4 小时前
代码审计-PHP原生开发篇&SQL注入&数据库监控&正则搜索&文件定位&静态分析
数据库·sql
qq_196976174 小时前
python的sql解析库-sqlparse
数据库·python·sql
淡定一生23335 小时前
数据仓库建模方法
大数据·数据库·数据仓库
洛菡夕5 小时前
MySQL故障排查与生产环境优化
数据库·mysql