第一部分:为什么要瞎折腾?(动机)
场景:如果不设计,直接把所有数据(老师、系别、楼、预算)塞进一张大表
inst_dept ,这就是个坏设计。
三大痛点 (Pitfalls) :
数据冗余 (Redundancy):物理系有100个老师,“物理系-Watson楼-预算9万”这条信息就要重复存100遍。
更新异常 (Update Anomaly):物理系搬家了?你必须修改那100行数据。漏改一行,数据就打架了(不一致)。
插入异常 (Incompleteness):新成立了“数学系”但还没招到老师(主键为空),结果这个系的信息连存都存不进去。
目标:把大表拆成小表(分解),消除冗余,同时保证数据不丢、逻辑不乱。
第二部分:拆表的工具——函数依赖 (FD)
定义:数据之间的逻辑绑定关系,记作
- 含义:只要知道了
的值,就能唯一确定 的值(就像知道了“身份证号”就能确定“姓名”)。
1. 三种需要消灭的“坏依赖”
| 类型 | 说明 | 通俗理解 |
|---|---|---|
| 平凡依赖 (Trivial) | 废话。这不需要处理。 | |
| 部分依赖 (Partial) | “抱半条大腿”。主键是(A+B),C只依赖A。 | |
| 传递依赖 (Transitive) | “隔山打牛”。老师定系,系定楼。老师跟楼的关系是隔了一层的。 |
2. 核心计算:闭包 (Closure)
(规则闭包):基于现有规则能推导出的所有规则。 (属性闭包):给定属性 ,它能决定的所有其他属性。 考点:如果
(全表属性),那 就是超键 (Superkey)。
3. 正则覆盖
(Canonical Cover ) ——
难点与计算核心
这是进行 3NF 分解 (Synthesis Algorithm) 的第一步,必须掌握。
3.1 动机与定义
目的:找到一个“最小、最精简”的函数依赖集
,它和原来的 逻辑等价(即闭包相同),但去掉了所有多余的废话。 要求:
等价性:
与 互相推导。 右侧单属性:
必须拆成 和 。 无多余属性:左边或右边没有任何属性是多余的。
3.2 判定“多余” (Extraneous Attributes)
左侧多余 (Left side):
- 对于
,如果 本身就成立(即 ),那么 就是多余的。
- 对于
右侧多余 (Right side) / 规则冗余:
- 对于
,如果删掉这条规则,利用剩下的规则还能推出 ,则它是废话。
- 对于
3.3 计算步骤 (Algorithm)
请严格按照以下步骤操作,不要跳步:
Flatten (拆分右侧):
- 将所有右边包含多个属性的依赖拆开。例:
变为 。
- 将所有右边包含多个属性的依赖拆开。例:
Remove Extraneous (L) (去左侧多余):
- 检查左边有多个属性的规则(如
)。试着去掉其中一个属性,用 计算剩余属性的闭包,看能否推出右边。
- 检查左边有多个属性的规则(如
Remove Redundant Rules (R) (去冗余规则):
- 逐条检查:如果删掉这条规则,利用剩下的规则能否将其推导出来?(通常是因为传递性
)。
- 逐条检查:如果删掉这条规则,利用剩下的规则能否将其推导出来?(通常是因为传递性
📝 实战例题
题目:
解:
拆分:
。 去左侧多余:检查
。因为 且 ,所以 多余。规则简化为 。 - 当前集合:
(去重后)。
- 当前集合:
去冗余规则:检查
。因为 且 可以推导出 。 结论:
是废话,删掉。 最终结果:
。
第三部分:好设计的标准——范式 (Normal Forms)
我们像爬楼梯一样,等级越高,要求越严。
第一范式 (1NF)
要求:格子不能混装。属性必须是原子的(Atomic)。
例子:电话号码栏不能填“138xxx, 139xxx”,必须拆成两条记录。
第二范式 (2NF)
要求:满足 1NF + 非主属性完全依赖于候选键。
消灭对象:部分依赖。
人话:如果你有两个老板(组合主键),你不能只听其中一个人的话。
第三范式 (3NF)
要求:满足 2NF + 非主属性不传递依赖于候选键。
消灭对象:传递依赖。
人话:非主键字段之间不能互相打架(互相决定)。
宽松条款:在
中,如果 B 是候选键的一部分(主属性),3NF 是允许的。
BCNF (Boyce-Codd范式)
要求:所有非平凡依赖
中, 必须是超键。 人话:想管事?先当老大。任何决定别人的字段,自己必须是全表的唯一标识。
第四部分:怎么拆?—— 分解算法 (Decomposition)
如果表设计不好,我们要把它拆解(Decomposition)。
两大原则:
无损连接 (Lossless-join):必须满足。拆开的表拼回去,数据不能多也不能少。
保持依赖 (Dependency Preservation):尽量满足。拆分后,规则不丢。
算法一:BCNF 分解算法(“找茬拆分法”)
核心逻辑:检查大表,发现违规就“切一刀”,直到没毛病。
📝 实战例题 (来源: Slide 63-66)
原表:
依赖 F:
候选键:HS (只有 HS 能决定所有人)
步骤演示:
第一刀:检查
。 CS 是候选键(HS)吗?不是。违规!
拆分:
表1(病灶):
—— 新表,CS是主键,符合BCNF。 表2(剩余):从原表踢掉 G,保留 CS。
。
第二刀:检查表2中的
。 在表2里,候选键还是 HS。C 不是候选键。违规!
拆分:
表2-1(病灶):
—— 符合BCNF。 表2-2(剩余):从表2踢掉 T,保留 C。
。
第三刀:检查表2-2中的
。 在表2-2里,HR 不是候选键。违规!
拆分:
表2-2-1(病灶):
—— 符合BCNF。 表2-2-2(剩余):从表2-2踢掉 C,保留 HR。
。
收工:检查表2-2-2
。 - 剩余依赖
,且 HS 是候选键。完美!
- 剩余依赖
最终结果:
算法二:3NF 分解算法(“规则合成法”)
核心逻辑:先把所有规则列出来,计算 Canonical Cover,按规则造表,最后检查有没有丢了“钥匙”。
📝 实战例题 (来源: Slide 57)
原表:
依赖 F:
候选键:
步骤演示:
Step 0:计算 Canonical Cover
- 此处给定的 F 已经是最小化的(无冗余),所以
。
- 此处给定的 F 已经是最小化的(无冗余),所以
Step 1:按规则造表
规则1
表1: 规则2
表2: 规则3
表3:
Step 2:检查候选键(关键!)
问题:现在的表1、2、3里,有没有哪张表包含了原表的老大(候选键 SNO, CNO)?
检查:表3
包含了 SNO 和 CNO。 结论:这就不用补表了。
(注:如果表3不存在,我们就必须人为新建一张表4 (SNO, CNO),否则以后没法查全表数据)。
Step 3:去重
- 没有哪张表是另一张表的子集。保留所有。
最终结果:
第五部分:考试速记卡 (Cheat Sheet)
| 概念 | 口诀/关键词 | 核心判定 |
|---|---|---|
| 1NF | 原子性 | 一格一值 |
| 2NF | 别抱半条腿 | 消除 非主属性 |
| 3NF | 别听中间商 | 消除 非主属性 |
| BCNF | 老大才能话事 | 左边必须是 Superkey |
| 正则覆盖 | 极简主义 | 等价、右边单属性、无废话 |
| 分解原则 | 无损连接 | |
| 3NF 算法 | 合成+补救 | 算正则覆盖 |
| BCNF 算法 | 找茬+切分 | 只要左边不是Key |
复习建议:
判定范式:先算候选键,然后看依赖关系是不是指向非主属性(3NF问题)或者左边不是键(BCNF问题)。
分解题目:如果让你分 3NF,第一步必须是Canonical Cover,最后别忘了检查候选键;如果让你分 BCNF,就盯着非 Key 的决定者切。
如果您喜欢我的文章,可以考虑打赏以支持我继续创作.