📊 先看一个"翻车"现场:
某个学校的数据库管理员,把全校的"学生 + 选课 + 系别"信息塞进了 一张大表 里:
SCD(学号, 姓名, 年龄, 系别, 系主任, 课程号, 成绩)
看起来挺方便:一张表搞定一切。但用着用着麻烦不断:同一个学生的姓名要存好几遍、新成立的系无法录入、毕业了一批学生连系都跟着没了……
👉 这章就是要讲:怎么判断一张表"设计得好不好",以及不好的话怎么把它拆开变好。
一、不规范的表会带来什么问题?重点
1.1 我们的"反面教材":SCD 表
整章我们用 一个例子贯穿到底:把所有信息塞在一张表里的 SCD。
SNo 学号 | SN 姓名 | Age 年龄 | Dept 系别 | MN 系主任 | CNo 课程号 | Score 成绩
主码(Primary Key)= (SNo, CNo) —— 学号和课程号组合起来唯一确定一行
SCD 表的实际数据
| SNo | SN | Age | Dept | MN | CNo | Score |
|---|---|---|---|---|---|---|
| S1 | 赵亦 | 17 | 计算机 | 刘伟 | C1 | 90 |
| S1 | 赵亦 | 17 | 计算机 | 刘伟 | C2 | 85 |
| S2 | 钱尔 | 18 | 信息 | 王平 | C5 | 57 |
| S2 | 钱尔 | 18 | 信息 | 王平 | C6 | 80 |
| S2 | 钱尔 | 18 | 信息 | 王平 | C7 | 空 |
| S2 | 钱尔 | 18 | 信息 | 王平 | C4 | 70 |
| S3 | 孙珊 | 20 | 信息 | 王平 | C1 | 75 |
| S3 | 孙珊 | 20 | 信息 | 王平 | C2 | 70 |
| S3 | 孙珊 | 20 | 信息 | 王平 | C4 | 85 |
| S4 | 李思 | 21 | 自动化 | 刘伟 | C1 | 93 |
🟡 黄色 = 主码 | 🔴 红色 = 重复出现的冗余数据
S2 同学的姓名"钱尔"、年龄"18"、系别"信息"、系主任"王平"重复出现了 4 次!
"信息"系的系主任"王平"重复了 6 次!
这就是数据冗余。
1.2 不规范的表带来的 4 个问题必考
仔细分析 SCD 表,会发现它有 4 个问题:
📦① 数据冗余
同一个数据 被存了很多遍。比如"信息系-王平"出现了 6 次。浪费存储空间。
🚫② 插入异常
新成立一个"机器人系",但还没招学生。想录入这个系都不行!因为主码 (SNo, CNo) 不能为空,没学生没课就插不进去。
🗑️③ 删除异常
"自动化系"只有李思一个人。如果李思毕业了,把他的记录删掉 —— "自动化系"和系主任"刘伟"也跟着消失了!
✏️④ 更新异常
"信息系"换了系主任。要把表里 所有"信息系"的系主任全改一遍,漏掉一处就会出现"同一个系,两个系主任"的不一致情况。
"存的多(冗余)、加不进(插入)、删错了(删除)、改不全(更新)"
1.3 问题的根源:一张表"啥都装"
SCD 这张表 "包罗万象" —— 既有学生信息、又有选课信息、又有系别信息。三件事挤在一张表里,必然出问题。
就像有人喜欢把所有东西都塞进 一个抽屉:袜子、笔、零食、文件……结果找东西找不到、东西很乱、还互相影响。
解决办法:"一事一地" —— 袜子放衣柜、笔放笔筒、零食放食品柜、文件放档案夹。每件事有自己的"位置"。
把 SCD 拆成 3 张专门的表:
- S(学生表):SNo, SN, Age, Dept
- SC(选课表):SNo, CNo, Score
- D(系别表):Dept, MN
这就是本章的目标:把不规范的"大杂烩表",通过规范化理论拆成多张规范的表。
那 怎么判断一张表是不是规范的?怎么 规范地拆分?这就要用到下面两个工具:
- 👉 函数依赖(描述属性之间的关系)
- 👉 范式(衡量规范化程度的标准)
二、函数依赖(关键工具)核心重点
2.1 函数依赖是什么?必考
如果知道了 X 就能唯一确定 Y,我们就说 "Y 函数依赖于 X",记作:
X → Y
还记得高中学的函数吗?给一个 x 值,就能算出唯一的 y 值。
函数依赖也是一样:给一个学号 SNo,就能查到唯一的姓名 SN —— 所以 SNo → SN。
SCD 例子里有哪些函数依赖?
SCD 关系中所有的函数依赖
F = { SNo → SN, SNo → Age, SNo → Dept, Dept → MN, (SNo, CNo) → Score }
读作:
- "学号决定姓名" —— SNo → SN
- "学号决定年龄" —— SNo → Age
- "系别决定系主任" —— Dept → MN
- "学号+课程号 共同决定 成绩" —— (SNo, CNo) → Score
2.2 三种函数依赖(最难也最重要的考点)必考
这是本章最难、也最容易考的部分。请认真理解。
✅ 完全函数依赖
左边的属性 "少一个都不行",必须全部都在场,才能决定右边。
⚠️ 部分函数依赖
左边的属性 "取一部分就够了",不需要全部都在场。
🔁 传递函数依赖
不是直接决定,而是 "绕一道弯":A→B,B→C,所以 A 通过 B "传递"决定 C。
用 SCD 例子讲透三种依赖
看 (SNo, CNo) → Score:
① 只给学号 SNo?不行,一个学生有多门成绩。
② 只给课程号 CNo?不行,一门课有很多人选。
③ 必须 同时给 SNo 和 CNo,才能确定一个唯一的 Score。
👉 所以 (SNo, CNo) → Score 是 完全函数依赖(左边一个都不能少)。
看 (SNo, CNo) → SN(姓名):
① 只给 SNo 行不行?行!学号已经能唯一确定姓名了。
② 也就是说,不需要 CNo 这个属性也能确定 SN。
👉 所以 (SNo, CNo) → SN 是 部分函数依赖(左边的"一部分"就够了)。
看 SNo → MN(系主任):
① SNo → Dept(学号决定系别)✅
② Dept → MN(系别决定系主任)✅
③ 通过 Dept 这个"中间属性",SNo 间接决定了 MN。
👉 所以 SNo → MN 是 传递函数依赖(绕了一道弯)。
完全 = 左边一个都不能少(左边是组合键时有意义)
部分 = 左边只要一部分就够了(左边是组合键的"子集")
传递 = 绕了一道弯,通过中间属性间接决定
📌 特别记住:如果左边只有一个属性(比如 SNo → SN),那 一定是完全函数依赖(因为没法"减少"了)。
三、范式:衡量"规范"的标准核心重点
3.1 范式是什么?
范式(Normal Form, NF) 是衡量关系模式 规范化程度 的标准。范式越高,越规范,问题越少。
1NF = 最基础(B1):能上路就行
2NF = 高一级(B2/A2):能开手动挡
3NF = 再高一级(A1):能开大客车
BCNF = 最高级(A1+):可以教别人开车
每升一级,要求越来越严格。本章重点学 1NF、2NF、3NF。
4 个范式的整体阶梯(从低到高)
范式是层层递进的:2NF 必须先满足 1NF,3NF 必须先满足 2NF。
所以判断时按顺序:先看 1NF?满足再看 2NF?满足再看 3NF?
3.2 第一范式(1NF):最基础的要求必考
如果一个关系中 每个属性都是不可再分的"原子值"(每个单元格只装一个值),就说这个关系满足第一范式(1NF)。
什么叫"不满足 1NF"?
"联系方式"列里塞了多个值:
| 姓名 | 联系方式 |
|---|---|
| 张三 | 手机:138... 邮箱:abc@ |
| 李四 | 手机:139... 邮箱:def@ |
把"联系方式"拆成两列:
| 姓名 | 手机 | 邮箱 |
|---|---|---|
| 张三 | 138... | abc@ |
| 李四 | 139... | def@ |
"每个单元格里只装一个原子值" —— 这是关系数据库最基本的要求。
实际上,MySQL 表自然就满足 1NF(不允许在一个字段里塞多个值),所以这一条平时不太需要特别注意。
回到我们的 SCD 表:每个字段都只有一个值,所以 SCD 满足 1NF。但是 ……
3.3 第二范式(2NF):消除"部分依赖"必考
如果关系 R 满足 1NF,且每个非主属性都"完全函数依赖"于主码(不存在部分依赖),就说 R 满足第二范式(2NF)。
SCD 满足 2NF 吗?
SCD 的主码是 (SNo, CNo),非主属性有:SN、Age、Dept、MN、Score。
SN(姓名):只要给 SNo 就能确定 SN,不需要 CNo。
👉 所以 (SNo, CNo) → SN 是 部分函数依赖 ❌ 违反 2NF!
同理 Age、Dept、MN 都只依赖 SNo,都是部分依赖。
只有 Score 是 完全依赖(必须 SNo + CNo 才能确定)。
因为存在 SN、Age、Dept、MN 等非主属性 对主码的部分函数依赖。
怎么把 SCD 拆成 2NF?
把 "只依赖 SNo 的属性" 单独拿出来组成一张表:
① SD 表(学生信息):SNo, SN, Age, Dept, MN —— 主码 SNo
② SC 表(选课):SNo, CNo, Score —— 主码 (SNo, CNo)
① SD 表:主码是 SNo(单属性)。只要左边是单属性,就一定是完全依赖(没法再"部分"了)。✅
② SC 表:主码是 (SNo, CNo),唯一的非主属性 Score 是完全依赖于 (SNo, CNo) 的。✅
但是!新拆出来的 SD 表里有 SNo → Dept → MN 这个链,还有传递函数依赖问题。继续……
3.4 第三范式(3NF):消除"传递依赖"必考
如果关系 R 满足 2NF,且每个非主属性都不"传递函数依赖"于主码,就说 R 满足第三范式(3NF)。
SD 表满足 3NF 吗?
SD 表:(SNo, SN, Age, Dept, MN),主码是 SNo。
① SNo → Dept(学号决定系别)✅
② Dept → MN(系别决定系主任)✅
③ 中间通过 Dept 转了一次 → SNo → MN 是 传递函数依赖!
❌ 违反 3NF!
怎么把 SD 拆成 3NF?
把传递链 "切断":把 Dept → MN 这部分单独拿出来组成一张表。
① S 表(学生):SNo, SN, Age, Dept —— 主码 SNo
② SC 表(选课):SNo, CNo, Score —— 主码 (SNo, CNo)
③ D 表(系别):Dept, MN —— 主码 Dept
这就是 PPT 里 "一事一地" 的体现:每张表只描述一件事 —— 学生表只管学生、选课表只管选课、系别表只管系别。
四、把 SCD 一步步分解:完整演示重点
4.1 完整分解过程
让我们把刚才的过程串起来,看完整的"规范化"流程:
SCD 经过两次分解,最终得到 3 张满足 3NF 的表
- 看 1NF:每个属性是不是原子的?(一般 MySQL 表都满足)
- 看 2NF:找出主码,看每个非主属性是不是完全依赖于主码?
⚠️ 只有当主码是 组合键 时,才可能违反 2NF;如果主码是单属性,自然满足 2NF。 - 看 3NF:是否存在 非主属性 → 非主属性 的传递?有就违反。
4.2 模式分解的两个原则了解
把一个表拆成多个表的过程叫 模式分解。分解结果不唯一,但好的分解应该满足:
分解后的表通过 JOIN(自然连接) 能 完全还原成原表 —— 信息没丢。
就像把一张大照片剪成几块,重新拼起来还能恢复原样。
分解后的所有表的函数依赖加起来,等价于原表的函数依赖 —— 关系没丢。
比如分解后还能通过几张表查到"S1 同学的系主任是谁"。
实际数据库设计时,能拆到 3NF 通常就足够了。无损连接和保持依赖在做设计时一般是自然满足的,不用刻意去验证(除非考研究生)。
五、本章小结
📋 三句话总结整章
- 问题:一个"啥都装"的烂表会有 4 个问题(数据冗余 + 插入/删除/更新异常)。
- 工具:函数依赖(X→Y)描述属性间的依赖;范式衡量规范化程度。
- 方法:通过模式分解,按"一事一地"原则把烂表拆成满足 3NF 的多张表。
本章必考点回顾
⭐ 期末考点(按出现频率排)
- 三种函数依赖的辨析:完全 / 部分 / 传递(最容易考!)
- 给一个关系,判断是几范式,并说明理由
- 模式分解:把不规范的关系分解成 3NF 的多张表
- 4 种异常的识别:插入异常、删除异常、更新异常、数据冗余
- 1NF / 2NF / 3NF 的定义(简答题)
课堂综合测验
在某个学生选课表 SC(学号, 课程号, 课程名, 成绩)中,主码是 (学号, 课程号)。下列说法 正确 的是?
✅ 正确:B
分析:课程名只依赖课程号(不依赖学号),这是 部分函数依赖 —— 违反 2NF。
所以应该拆成:SC(学号, 课程号, 成绩)+ C(课程号, 课程名)。
关系 R(A, B, C, D),主码为 A,函数依赖集 F = {A→B, B→C, A→D}。这个关系:
✅ 正确:B
判断步骤:
① 1NF:所有属性原子,✓ 满足
② 2NF:主码 A 是单属性,所以一定是完全依赖(没法部分),✓ 满足
③ 3NF:A→B,B→C,存在传递依赖 A→C(通过 B 中转),❌ 违反
下列哪一种 不属于 不规范关系会导致的问题?
✅ 正确:C · 死锁
死锁是 并发控制 的问题(第 9 章学过),和规范化没关系。
不规范关系导致的 4 个问题是:数据冗余 + 插入异常 + 删除异常 + 更新异常。
关于 SCD(SNo, SN, Age, Dept, MN, CNo, Score),主码 (SNo, CNo)。下面哪一组分解 是最合理 的(达到 3NF)?
✅ 正确:B
分析:
A 只到 2NF,因为 SD 表里 SNo→Dept→MN 仍有传递依赖。
C 还是原表。
D 拆得太碎了(SN 和 Age 不需要单独成表)。
B 是教材标准答案 —— "一事一地"原则的典型应用。
🚀 下节课:练习课
下节课我们会通过 大量练习 巩固今天的知识:
- 📝 判断函数依赖:给一组属性,找出完全/部分/传递依赖
- 📊 判断范式等级:给一个关系,说出它是几范式
- 🔧 模式分解练习:把不规范的关系拆成 3NF 的多张表(必考!)
- 📈 综合应用题:给一个真实业务场景,做完整的规范化设计
本章是 纯理论 + 推导,没有上机部分。下节课请带上纸笔。