关系模式的规范化理论

怎样设计一个"好"的表?—— 数据库设计的"金标准"

🎯 本节课你需要掌握的(按重要程度排序)
  • 不规范的表会造成的 4 个问题(数据冗余 + 三种异常)必考
  • 函数依赖的概念和符号 X→Y 必考
  • 三种函数依赖:完全 / 部分 / 传递(最难分清,必考!)必考
  • 1NF、2NF、3NF 的定义和判断 必考
  • 把不规范的表分解为规范的表 必考

📊 先看一个"翻车"现场:

某个学校的数据库管理员,把全校的"学生 + 选课 + 系别"信息塞进了 一张大表 里:

SCD(学号, 姓名, 年龄, 系别, 系主任, 课程号, 成绩)

看起来挺方便:一张表搞定一切。但用着用着麻烦不断:同一个学生的姓名要存好几遍、新成立的系无法录入、毕业了一批学生连系都跟着没了……

👉 这章就是要讲:怎么判断一张表"设计得好不好",以及不好的话怎么把它拆开变好

一、不规范的表会带来什么问题?重点

1.1 我们的"反面教材":SCD 表

整章我们用 一个例子贯穿到底:把所有信息塞在一张表里的 SCD

📋 SCD 表的属性含义

SNo 学号 | SN 姓名 | Age 年龄 | Dept 系别 | MN 系主任 | CNo 课程号 | Score 成绩

主码(Primary Key)= (SNo, CNo) —— 学号和课程号组合起来唯一确定一行

SCD 表的实际数据

SNo SN Age Dept MN CNo Score
S1赵亦17计算机刘伟C190
S1赵亦17计算机刘伟C285
S2钱尔18信息王平C557
S2钱尔18信息王平C680
S2钱尔18信息王平C7
S2钱尔18信息王平C470
S3孙珊20信息王平C175
S3孙珊20信息王平C270
S3孙珊20信息王平C485
S4李思21自动化刘伟C193

🟡 黄色 = 主码 | 🔴 红色 = 重复出现的冗余数据

👀 一眼就能看到的问题

S2 同学的姓名"钱尔"、年龄"18"、系别"信息"、系主任"王平"重复出现了 4 次!

"信息"系的系主任"王平"重复了 6 次!

这就是数据冗余

1.2 不规范的表带来的 4 个问题必考

仔细分析 SCD 表,会发现它有 4 个问题

📦① 数据冗余

同一个数据 被存了很多遍。比如"信息系-王平"出现了 6 次。浪费存储空间

🚫② 插入异常

新成立一个"机器人系",但还没招学生。想录入这个系都不行!因为主码 (SNo, CNo) 不能为空,没学生没课就插不进去。

🗑️③ 删除异常

"自动化系"只有李思一个人。如果李思毕业了,把他的记录删掉 —— "自动化系"和系主任"刘伟"也跟着消失了

✏️④ 更新异常

"信息系"换了系主任。要把表里 所有"信息系"的系主任全改一遍,漏掉一处就会出现"同一个系,两个系主任"的不一致情况。

⭐ 4 个问题速记

"存的多(冗余)、加不进(插入)、删错了(删除)、改不全(更新)"

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

🌰 中学的函数 y = f(x)

还记得高中学的函数吗?给一个 x 值,就能算出唯一的 y 值。

函数依赖也是一样:给一个学号 SNo,就能查到唯一的姓名 SN —— 所以 SNo → SN

SCD 例子里有哪些函数依赖?

SNo SN(姓名) Age(年龄) Dept(系别) Dept MN(系主任) (SNo, CNo) Score(成绩) 学号决定学生信息 系别决定系主任 学号+课程号 决定 成绩

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 个范式的整体阶梯(从低到高)

第一范式1NF
属性原子化 每个属性都不可再分(每个单元格只能装一个值)
第二范式2NF
在 1NF 基础上 + 消除"部分函数依赖" 每个非主属性都完全函数依赖于主码
第三范式3NF
在 2NF 基础上 + 消除"传递函数依赖" 非主属性不能传递函数依赖于主码
了解BCNF
在 3NF 基础上 + 主属性也不能部分依赖于码 了解概念即可,不深入
⚠️ 重要原则

范式是层层递进的:2NF 必须先满足 1NF,3NF 必须先满足 2NF

所以判断时按顺序:先看 1NF?满足再看 2NF?满足再看 3NF?

3.2 第一范式(1NF):最基础的要求必考

📖 1NF 的定义

如果一个关系中 每个属性都是不可再分的"原子值"(每个单元格只装一个值),就说这个关系满足第一范式(1NF)。

什么叫"不满足 1NF"?

❌ 不满足 1NF 的表

"联系方式"列里塞了多个值:

姓名联系方式
张三手机:138... 邮箱:abc@
李四手机:139... 邮箱:def@
✅ 满足 1NF 的表

把"联系方式"拆成两列:

姓名手机邮箱
张三138...abc@
李四139...def@
💡 一句话理解 1NF

"每个单元格里只装一个原子值" —— 这是关系数据库最基本的要求。

实际上,MySQL 表自然就满足 1NF(不允许在一个字段里塞多个值),所以这一条平时不太需要特别注意。

回到我们的 SCD 表:每个字段都只有一个值,所以 SCD 满足 1NF。但是 ……

3.3 第二范式(2NF):消除"部分依赖"必考

📖 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 才能确定)。

❌ 结论:SCD 不满足 2NF

因为存在 SN、Age、Dept、MN 等非主属性 对主码的部分函数依赖

怎么把 SCD 拆成 2NF?

"只依赖 SNo 的属性" 单独拿出来组成一张表:

📦 分解结果(满足 2NF)

SD 表(学生信息):SNo, SN, Age, Dept, MN —— 主码 SNo

SC 表(选课):SNo, CNo, Score —— 主码 (SNo, CNo)

💬 为什么这样分就 OK 了?

SD 表:主码是 SNo(单属性)。只要左边是单属性,就一定是完全依赖(没法再"部分"了)。✅

SC 表:主码是 (SNo, CNo),唯一的非主属性 Score 是完全依赖于 (SNo, CNo) 的。✅

但是!新拆出来的 SD 表里有 SNo → Dept → MN 这个链,还有传递函数依赖问题。继续……

3.4 第三范式(3NF):消除"传递依赖"必考

📖 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 这部分单独拿出来组成一张表。

📦 最终分解结果(全部满足 3NF)

S 表(学生):SNo, SN, Age, Dept —— 主码 SNo

SC 表(选课):SNo, CNo, Score —— 主码 (SNo, CNo)

D 表(系别):Dept, MN —— 主码 Dept

这就是 PPT 里 "一事一地" 的体现:每张表只描述一件事 —— 学生表只管学生、选课表只管选课、系别表只管系别。

四、把 SCD 一步步分解:完整演示重点

4.1 完整分解过程

让我们把刚才的过程串起来,看完整的"规范化"流程:

原始:SCD(包罗万象的烂表) SNo, SN, Age, Dept, MN, CNo, Score 满足 1NF(每个值都是原子的)✓ 分析:存在部分依赖(违反 2NF) SN/Age/Dept/MN 只依赖 SNo,不依赖 CNo 把"只依赖 SNo 的"和"依赖 (SNo, CNo) 的"分开 中间:SD 表(满足 2NF) SNo, SN, Age, Dept, MN SC 表(已满足 3NF ✓) SNo, CNo, Score · 主码 (SNo, CNo) 还存在传递依赖(违反 3NF) 切断传递链 SNo→Dept→MN S 表(学生 ✓ 3NF) SNo, SN, Age, Dept 主码 SNo D 表(系别 ✓ 3NF) Dept, MN 主码 Dept SC 表(之前已经分出来) SNo, CNo, Score

SCD 经过两次分解,最终得到 3 张满足 3NF 的表

📋 范式判断三步走
  1. 看 1NF:每个属性是不是原子的?(一般 MySQL 表都满足)
  2. 看 2NF:找出主码,看每个非主属性是不是完全依赖于主码?
    ⚠️ 只有当主码是 组合键 时,才可能违反 2NF;如果主码是单属性,自然满足 2NF。
  3. 看 3NF:是否存在 非主属性 → 非主属性 的传递?有就违反。

4.2 模式分解的两个原则了解

把一个表拆成多个表的过程叫 模式分解。分解结果不唯一,但好的分解应该满足:

🔗 无损连接

分解后的表通过 JOIN(自然连接)完全还原成原表 —— 信息没丢。

就像把一张大照片剪成几块,重新拼起来还能恢复原样。

🔄 保持函数依赖

分解后的所有表的函数依赖加起来,等价于原表的函数依赖 —— 关系没丢。

比如分解后还能通过几张表查到"S1 同学的系主任是谁"。

💡 这两点了解就行

实际数据库设计时,能拆到 3NF 通常就足够了。无损连接和保持依赖在做设计时一般是自然满足的,不用刻意去验证(除非考研究生)。

五、本章小结

📋 三句话总结整章

🎯 核心逻辑链
  • 问题:一个"啥都装"的烂表会有 4 个问题(数据冗余 + 插入/删除/更新异常)。
  • 工具函数依赖(X→Y)描述属性间的依赖;范式衡量规范化程度。
  • 方法:通过模式分解,按"一事一地"原则把烂表拆成满足 3NF 的多张表。

本章必考点回顾

⭐ 期末考点(按出现频率排)

  1. 三种函数依赖的辨析:完全 / 部分 / 传递(最容易考!)
  2. 给一个关系,判断是几范式,并说明理由
  3. 模式分解:把不规范的关系分解成 3NF 的多张表
  4. 4 种异常的识别:插入异常、删除异常、更新异常、数据冗余
  5. 1NF / 2NF / 3NF 的定义(简答题)

课堂综合测验

第 1 题

在某个学生选课表 SC(学号, 课程号, 课程名, 成绩)中,主码是 (学号, 课程号)。下列说法 正确 的是?

A. 该关系满足 3NF
B. 课程名只依赖课程号,存在部分依赖,违反 2NF
C. 该关系存在传递依赖
D. 该关系不满足 1NF

✅ 正确:B

分析:课程名只依赖课程号(不依赖学号),这是 部分函数依赖 —— 违反 2NF。

所以应该拆成:SC(学号, 课程号, 成绩)+ C(课程号, 课程名)。

第 2 题

关系 R(A, B, C, D),主码为 A,函数依赖集 F = {A→B, B→C, A→D}。这个关系:

A. 满足 3NF
B. 只满足 2NF(违反 3NF,因为存在传递依赖 A→B→C)
C. 只满足 1NF(违反 2NF)
D. 不满足 1NF

✅ 正确:B

判断步骤:

① 1NF:所有属性原子,✓ 满足

② 2NF:主码 A 是单属性,所以一定是完全依赖(没法部分),✓ 满足

③ 3NF:A→B,B→C,存在传递依赖 A→C(通过 B 中转),❌ 违反

第 3 题

下列哪一种 不属于 不规范关系会导致的问题?

A. 数据冗余
B. 插入异常
C. 死锁
D. 更新异常

✅ 正确:C · 死锁

死锁是 并发控制 的问题(第 9 章学过),和规范化没关系。

不规范关系导致的 4 个问题是:数据冗余 + 插入异常 + 删除异常 + 更新异常

第 4 题

关于 SCD(SNo, SN, Age, Dept, MN, CNo, Score),主码 (SNo, CNo)。下面哪一组分解 是最合理 的(达到 3NF)?

A. SD(SNo, SN, Age, Dept, MN) + SC(SNo, CNo, Score)
B. S(SNo, SN, Age, Dept) + D(Dept, MN) + SC(SNo, CNo, Score)
C. SCD(全部属性放一起,只是 1NF)
D. S(SNo) + N(SN, Age) + D(Dept, MN) + SC(SNo, CNo, Score)

✅ 正确:B

分析:

A 只到 2NF,因为 SD 表里 SNo→Dept→MN 仍有传递依赖。

C 还是原表。

D 拆得太碎了(SN 和 Age 不需要单独成表)。

B 是教材标准答案 —— "一事一地"原则的典型应用。

🚀 下节课:练习课

下节课我们会通过 大量练习 巩固今天的知识:

  • 📝 判断函数依赖:给一组属性,找出完全/部分/传递依赖
  • 📊 判断范式等级:给一个关系,说出它是几范式
  • 🔧 模式分解练习:把不规范的关系拆成 3NF 的多张表(必考!)
  • 📈 综合应用题:给一个真实业务场景,做完整的规范化设计

本章是 纯理论 + 推导,没有上机部分。下节课请带上纸笔。