参考:
- 数据库技术知识点(一)IDEFO需求建模方法、解释实体、实体型、实体集的区别、完全函数依赖、部分函数依赖、传递函数、平凡函数依赖、非平凡函数依赖举例、超码、主码、候选码的概念与区分
- 《数据库系统概论》
码
超码
- 定义:超码也叫做超级码,是一个或多个属性的集合,这些属性可以让我们在一个实体集(所谓的实体集就是student表中多条记录的集合)中唯一地标识一个实体。如果K是一个超码,那么K的任意超集也是超码,也就是说如果K是超码,那么所有包含K的集合也是超码。
- 例如:因为通过Id或者Sno可以找到唯一一个学生,所以(Id)和(Sno)是超码,同理(Id ,Sno)、 (Id,Sno, Name)、(Id,Sno ,Name , Sex)、(Sno, Name)、 (Sno, Name,Sex)也是超码
所谓超集是集合论的术语,A ⊇ B,则 A 集是 B 的超集,也就是说 B 的所有元素 A 里都有,但 A 里的元素 B 就未必有
候选码
- 定义:超码包括候选码,虽然超码可以唯一标识一个实体,但是可能大多数超码中含有多余的属性,所以我们需要候选码。若关系中的一个属性或属性组的值能够唯一地标识一个元组,且它的真子集不能唯一的标识一个元组,则称这个属性或属性组做候选码。
- 例如:在上例中,只有(Id)或者(Sno)是候选码。如果 Sex 和 Name 可以唯一标识一个学生,则(Name, Sex)也为候选码,但是,Sex 和 Name并不能唯一标识一个学生,这与现实生活是违反的,因为现实有同名同姓的人,则(Name,Sex)不能作为候选码。
子集比真子集范围大,子集是包括本身的元素的集合,真子集是除本身的元素的集合
主码
- 定义:一个表的候选码可能有多个,从这些个候选码中选择一个做为主码,至于选择哪一个候选码,这个是无所谓的,只要是从候选码中选的就行。
- 例如:在上例中,只有(Id)或者(Sno)是候选码。可以选 Id 或者 Sno 为主码。
函数依赖
- 定义:如果 r(R) 上的每个合法实例满足函数依赖 X—>Y :对实例中所有元组对, t1 和 t2 ,若 t1[X] = t1[Y],则 t1[Y] = t2[Y]。则我们说该函数依赖在模式 r(R) 上成立
- 例如:在学生表(学号,姓名,年级)中,学号—>姓名,对所有元组对 t1 和 t2,若
非平凡函数依赖
- 定义:若X->Y,但Y不是X的子集,就是非平凡函数依赖。
- 例如:在学生表(学号,姓名,年级)中通过(学号,姓名)可以推出(这个学生所在的年级),但(年级)不是(学号,姓名)的子集,这是非平凡函数依赖(学号,姓名)就是一个X,(年级)就是一个Y。
平凡函数依赖
- 定义:若X->Y,且Y是X的子集(对任一关系模式,平凡函数依赖必然成立),就是平凡函数依赖。
- 例如:在学生表(学号,姓名,年级)中.(学号,姓名)可以推出学号和姓名其中的任何一个;这就是平凡函数依赖。直白点说,就是只要Y是X的子集,Y就依赖于X。
完全函数依赖
- 定义:若X->Y,并且对于X的任何一个真子集X',都有Y不函数依赖于X',则称Y对X完全函数依赖。
- 例如:通过(学生学号,选修课程名)可以得到(该生本门选修课程的成绩),而通过单独的(学生学号)或者单独的(选修课程名)都无法得到该成绩,则说明(该生本门选修课程的成绩)完全依赖于(学生学号,选修课程名)
部分函数依赖
- 定义:若X->Y,但Y不完全函数依赖于X,则称Y对X部分函数依赖。
- 例如:通过(学生学号,课程号)可以得到(该生姓名),而通过单独的(学生学号)已经能够得到(该生姓名),则说明(该生姓名)部分依赖于(学生学号,课程号);又比如,通过(学生学号,课程号)可以得到(课程名称),而通过单独的(课程号)已经能够得到(课程名称),则说明(课程名称)部分依赖于(学生学号,课程号)。(部分依赖会造成数据冗余及各种异常。)
传递函数依赖
- 定义:如果X->Y,Y不是X的子集且Y不函数依赖于X;Y->Z,Z不是Y的子集且Z不依赖于Y。X->Z 称Z对X传递函数依赖。
- 例如:在关系R(学号,宿舍,费用)中,通过(学号)可以得到(宿舍),通过(宿舍)可以得到(费用),而反之都不成立,则存在传递依赖(学号)->(费用)。(传递依赖也会造成数据冗余及各种异常。)
函数依赖理论
函数依赖集的闭包
逻辑蕴含
- 定义:给定模式上的函数依赖集 F,我们可以证明某些其他的函数依赖在模式上也成立。我们称这些函数依赖被 F “逻辑蕴含”。
- 示例:假设我们给定关系模式 r(A, B, C, G, H, I) 及函数依赖集:A—>B,A—>C,CG—>H,CG—>I,B—>H。那么函数依赖 A—>H 被逻辑蕴含。也就是说,我们可以证明,一个关系只要满足上面给定的函数依赖集,这个关系也一定满足 A—>H。
函数依赖集(F)的闭包(F+)
函数依赖集 F 的闭包是被 F 逻辑蕴含的所有函数依赖的集合,记作 F+
用 Armstrong 公理找出闭包 F+
- 自反律(reflexivity):如果 α⊆β,那么 α—>β
- 增广律(augmentation):如果 α—>β,那么 αγ—>βγ
- 传递律(transitivity):如果 α—>β 且 β—>γ,那么 α—>γ
直接用 Armstrong 公理找出闭包需要指数时间
Armstrong 公理的简化和附加规则
- 合并规则(union):如果 α—>β 且 α—>γ,那么 α—>βγ
- 分解规则(decomposition):如果 α—>βγ,那么 α—>β α—>γ
- 伪传递规则(pseudotransitivity):如果 α—>β 且 βγ—>δ,那么 αγ—>δ
计算 F+ 的过程
F+ = F
repeat
for each F+ 中的函数依赖 f
在 f 上运用自反律和增补律,将结果加到 F+ 中
for each F+ 中的一对函数依赖 f1 和 f2
如果 f1 和 f2 可以使用传递律结合起来,将结果加入到 F+ 中
until F+ 不再发生变化
示例:
这个算法是 NP 问题
属性集的闭包
定义:令 α 为一个属性集。我们将函数依赖 F 下被 α 函数确定的所有属性的集合称为 F 下 α 的闭包,记为 α+
【计算属性集 α 的闭包 α+ 过程】
result := α
repeat
for each 函数依赖 β—>γ in F do
begin
if β⊆result then result:=result ∪ γ
end
until result不再发生变化
示例:
R={A, B, C, G, H, I}
F={AB, AC, CGH, CGI, BH}
计算属性集 (AG) 的闭包 (AG)+
- result = AG
- result = ABCG(A—>C and A—>B)
- result = ABCGH(CG—>H and CG ⊆ ABCG)
- result = ABCGHI(CG—>I and CG ⊆ ABCGH)
(AG) 是 R 的超码吗?
- 是的,(AG)可以唯一确定 R 中的一个实体(ABCGHI)
(AG) 是 R 的候选码吗?
- 是的,(AG)的真子集 (A)或 (G)都不能唯一确定 R 中的一个实体。所以(AG)是最小集了。
【属性闭包算法的用途】
- 检验 α 是否为超码
- 计算 α+,检查 α+ 是否包含R中的所有属性
- 检验函数依赖 α—>β 是否成立(换句话说,α—>β 是否在 F+ )
- 计算 α 的闭包 α+,检查闭包 α+ 中是否包含 β
- 这种检查简单而有效
- 计算 F+ 的另一种方法
- 对于任意的 γ⊆R,我们找出 γ+
- 对于任意的 S⊆γ+,我们的输出一个函数依赖 γ—>S
正则覆盖
无关属性的定义
如果去除函数依赖中的一个属性不改变该函数依赖集的闭包,则称该属性是无关的
形式化定义如下:考虑函数依赖集合 F 以及 F 中的函数依赖 α—>β
- 如果 A ∈ α 并且 F 逻辑蕴含 (F - {α—>β}) ∪ {(α - A)—>β},则属性 A 在 α 中是无关的
- 如果 A ∈ β 并且函数依赖集 (F - {α—>β}) ∪ { α —> (β-A)} 逻辑蕴含 F,则属性 A 在 β 中是无关的
例子:
例1:给定 F = {A—>C, AB—>C} ,B 在 AB—>C 中是无关属性
- 因为 B ∈ AB (AB—>C 中),并且 F 逻辑蕴含【 {A—>C} (F 去掉 AB—>C)∪ {A—>C} (AB—>C 左边去掉 B)】
例2:给定 F = {A—>C, AB—>CD} ,C 在 AB—>CD 中是无关属性
- 因为 C ∈CD (AB—>CD 中),并且函数依赖集 【 {A—>C}(F 去掉 AB—>CD)∪ {AB—>D} (AB—>CD 右边去掉 D)】逻辑蕴含 F
判断一个属性是否是无关属性的步骤
考虑一个函数依赖集,α—>β 在 F 中
如果 A ∈ α ,检验 A 在 α 中是否是无关属性:
- 在函数依赖集 F 中计算 ({α}-A)+
- 检查 ({α}-A)+ 是否包含 β ,如果包含 β,那么 A 就是无关属性
- 简而言之: α 去掉 A 的属性闭包如果包含 β,也就是说 α 不用 A 属性就可以推出 β,A 就是无关属性
如果 A ∈ β,检验 A 在 β 中是否是无关属性:
- 仅使用函数依赖集 (F - {α—>β}) ∪ { α —> (β-A)} 计算 α+
- 检查 α+ 中是否包含 A,如果包含,那么 A 就是无关属性
- 简而言之:把 F 中的 α—>β 右边的 A 去掉,计算 α 的闭包如果包含 A,也就是说 α 用别的函数依赖就可以推出 A,A 就是右边的无关属性
正则覆盖的定义
F 的 正则覆盖 Fc 是一个函数依赖集:
- F 逻辑蕴含 Fc 中的所有依赖
- Fc 逻辑蕴含 F 中的所有依赖
- Fc 中任何函数依赖都不含无关属性
- Fc 中函数依赖的左半部都是唯一的
计算正则覆盖
算法(可以先忽略,重点看下面的解法2):
Fc = F
repeat
使用结合律将 Fc 中所有形如 α—>β1 和 α—>β2 的依赖替换为 α—>β1β2
在 Fc 中寻找一个函数依赖 α—>β,它在 α 或 β 中具有一个无关属性
如果找到一个无关属性,则将它从 Fc 中的 α—>β 中删除
utile Fc 不变
例1
R = {A, B, C}
F = {A—>BC, B—>C, A—>B, AB—>C}
解法1:
- 用结合律将 A—>BC 和 A—>B 变为 A—>BC
- 集合现在是 {A—>BC, B—>C, AB—>C}
- A 是 AB—>C 中的无关属性
- 因为去掉 A 的 B 也能独立推出来 C
- 集合现在是 {A—>BC, B—>C}
- C 是 A—>BC 中的无关属性
- 因为去掉 C 后,在 F' = {A—>B, B—>C} 中, A 的属性闭包是 (B,C),已经包含了 C
- 所以正则覆盖是 {A—>B, B—>C}
解法2(推荐):
- 先把右边复合的拆开 {A—>B, A—>C, B—>C, AB—>C}
- 去掉 A—>B,在 {A—>C, B—>C, AB—>C} 计算 A 的闭包为 (A), 不包含 B
- 所以这个不能去掉,现在还是 {A—>B, A—>C, B—>C, AB—>C}
- 去掉 A—>C,在 {A—>B, B—>C, AB—>C} 计算 A 的闭包为 (ABC),包含C
- 所以这个可以去掉,现在是 {A—>B, B—>C, AB—>C}
- 去掉 B—>C,在 {A—>B, AB—>C} 计算 B 的闭包为 (B),不包含C
- 所以这个不可以去掉,现在是 {A—>B, B—>C, AB—>C}
- 去掉 AB—>C,在 {A—>B, B—>C} 计算 (AB) 的闭包为 (ABC),包含C
- 所以这个可以去掉,现在是 {A—>B, B—>C}
- Fc = {A—>B, B—>C}
例2
正则覆盖不是唯一的,与你计算时的顺序有关,例如:
R = {A, B, C}
F = {A—>B, B—>A, B—>C, A—>C, C—>A}
解法1(按照原来的顺序):
- 去掉 A—>B,在 {B—>A, B—>C, A—>C, C—>A} 计算 A 的闭包为 (AC),不包含 B,这个不能去掉。
- 去掉 B—>A,在 {A—>B, B—>C, A—>C, C—>A} 计算 B 的闭包为(BCA),包含A,这个可以去掉。
- 去掉 B—>C,在 {A—>B, A—>C, C—>A} 计算 B 的闭包为(B),不包含 C,这个不能去掉。
- 去掉 A—>C,在 {A—>B, B—>C, C—>A} 计算 A 的闭包为 (A,B,C),包含C,这个可以去掉。
- 去掉 C—>A,在 {A—>B, B—>C} 计算 C 的闭包为(C),不包含A,这个不能去掉。
- 所以 Fc = {A—>B, B—>C, C—>A}
解法2(变一下顺序):
将计算顺序变为 {A—>B, B—>C, B—>A, A—>C, C—>A}
- 去掉 A—>B,在 {B—>C, B—>A, A—>C, C—>A} 计算 A 的闭包为 (AC),不包含 B,这个不能去掉。
- 去掉 B—>C,在 {A—>B, B—>A, A—>C, C—>A} 计算 B 的闭包为(BAC),包含 C,这个可以去掉。
- 去掉 B—>A,在 {A—>B, A—>C, C—>A} 计算 B 的闭包为(B),不包含A,这个不能去掉。
- 去掉 A—>C,在 {A—>B, B—>A, C—>A} 计算 A 的闭包为 (A,B),不包含C,这个不能去掉。
- 去掉 C—>A,在 {A—>B, B—>A, A—>C} 计算 C 的闭包为(C),不包含A,这个不能去掉。
- 所以 Fc = {A—>B, B—>A, A—>C, C—>A}
寻找候选码
算法
- 求出 F 的闭包 Fc,一下全部都基于 Fc
- 将 R 中所有属性分为两部分:
- XLN 代表 L 和 N 类型(只在左边的,和 左右两边都不在的)
- YLR 代表 LR 类型(左右两边都在的)
- 计算 X 的闭包 X+ , 如果 X+ 包含 R 中的所有属性,那么 X 是 R 的唯一候选码,然后去第 5 步,否则去第 3 步
- 将 A 从 Y 中移除,如果 (XA)+ 包含 R 中的所有属性,那么 XA 是 R 的一个候选码。然后继续从 Y 中取一个属性,继续这个过程,直到 Y 中所有属性都被测试过了
- 如果在步骤 3 中所有的候选码都被发现了,那么就去步骤 5。否则从 Y 中取 2 或 3 或更多属性,然后并到 X 计算属性闭包,直到属性闭包包含 R 中的所有属性
- 完成了,输出结果。
例1:
R<U,F>, U={X,Y,Z,W}
F={W—>Y, Y—>W, X—>WY, Z—>WY, XZ—>W}
- Fc = {W—>Y, Y—>W, X—>Y, Z—>Y}
- XLN = XZ, YLR = YW
- XLN+ = {X,Y,Z,W} = U ,所以 (XZ) 是 R 的唯一候选码
例2:
R<U,F>, U={A,B,C,D}
F={AB—>C, C—>D, D—>A}
- Fc = {AB—>C, C—>D, D—>A}
- XLN = B, YLR = ACD
- XLN+ = {B} ≠ U
- (AB)+={ABCD}=U,(BC)+={ABCD}=U,(BD)+={ABCD}=U
- (AB)、(BC)、(CD) 都是 R 的候选码
例4:
R<U,F>, U={O,B,I,S,Q,D}
F={S—>D, D—>S, I—>B, B—>I, B—>O,O—>B}
- Fc = {S—>D, D—>S, I—>B, B—>IO,O—>B}
- XLN = Q, YLR = SDBIO
- XLN+ = {B} ≠ U
- 加一个:(QS)+={QSD},(QD)+={QSD},(QB)+={QBIO},(QI)+={QBIO},(QO)+={QBIO} 都不等于 U
- 所以加两个:(QSO)+、(QSB)+、(QSI)+、(QSD)+、(QDO)+、(QDB)+、(QDI)+、(QDS)+、(QBO)+、(QBO)+、(QBI)+、(QBS)+、(QBD)+、(QIO)+、(QIB)+、(QSI)+、(QID)+、(QOB)+、(QOI)+、(QOS)+、(QOD)+、
- (QSO)、(QDO)、(QSB)、(QDB)、(QSI)、(QDI) 都是 R 的候选码
无损分解
如果我们把 r 分解为 R1 和 R2,然后计算 R1 和 R2 的自然连接,如果我们仍然得到一模一样的 r,这样的分解称为无损分解。
考虑一个有损分解:employee(ID, name, street, city, salary) 分解为:
- employee1(ID, name)
- employee2(name, street, city, salary)
- employee1 和 employee2 自然连接后,当有两个或多个雇员具有相同的姓名时,连接的结果丢失了关于哪个雇员标识和哪个地址及工资相关联的信息
函数依赖和无损分解
R1 和 R2 是 R 的分解,如果以下函数依赖至少一个属于 F+,那么就是无损分解:
- R1 ∩ R2 —> R1
- R1 ∩ R2 —> R2
换句话说,如果 R1 ∩ R2 是 R1 或 R2 的超码,R 上的分解就是无损分解。
检查是否无损分解
例1:
R<U,F>, U={A,B,C,D,E}
F={A—>C, B—>C, C—>D, DE—>C, CE—>A}
分解:{R1(A, D), R2(A, B), R3(B, E), R4(C, D, E), R5(A, E)}
1、先按分解填充表格
A | B | C | D | E | |
(AD) | a | a | |||
(AB) | a | a | |||
(BE) | a | a | |||
(CDE) | a | a | a | ||
(AE) | a | a |
2、A—>C 无 A, C 两列都有 a 的
3、B—>C 无 B, C 两列都有 a 的
4、C—>D 有 C, D 两列都有 a 的,所以把 D 列都填上 a
A | B | C | D | E | |
(AD) | a | a | |||
(AB) | a | a | a | ||
(BE) | a | a | a | ||
(CDE) | a | a | a | ||
(AE) | a | a | a |
5、DE—>C 有 D, E, C 三列都有 a 的,所以把 C 列都填上 a
A | B | C | D | E | |
(AD) | a | a | a | ||
(AB) | a | a | a | a | |
(BE) | a | a | a | a | |
(CDE) | a | a | a | ||
(AE) | a | a | a | a |
6、CE—>A 有 C, E, A 三列都有 a 的,所以把 A 列都填上 a
A | B | C | D | E | |
(AD) | a | a | a | ||
(AB) | a | a | a | a | |
(BE) | a | a | a | a | a |
(CDE) | a | a | a | a | |
(AE) | a | a | a | a |
有一行全为 a,所以是无损分解
例2:
R<U,F>, U={A,B,C,D}
F={A—>B, A—>C, C—>D}
分解:{R1(A, B), R2(B, C), R3(C, D)}
1、先按分解填充表格
A | B | C | D | |
(AB) | a | a | ||
(BC) | a | a | ||
(CD) | a | a |
2、A—>B 有 A, B 两列都有 a 的,所以把 B 列都填上 a
A | B | C | D | |
(AB) | a | a | ||
(BC) | a | a | ||
(CD) | a | a | a |
3、A—>C 无 A, C 两列都有 a 的
4、C—>D 有 C, D 两列都有 a 的,所以把 D 列都填上 a
A | B | C | D | |
(AB) | a | a | a | |
(BC) | a | a | a | |
(CD) | a | a | a |
没有一行全为 a,所以是有损分解
保持依赖
如果 F 中的每一个函数依赖都可以在分解得到的某一个关系上验证,那么这个分解就是保持依赖的。
在分解不能保持依赖时,对函数依赖的检查可能会用到连接,例如 α—>β 分解后,如果没有一个关系同时包含 α, β,就需要将包含它们的两个关系连接起来,再将 α, β 投影出来。
第一范式 1NF(First Normal Form)
要求:
- 没有多值属性
- 每个属性都是原子的
以下是非原子属性的一些例子:
- 名字的集合 是非原子的:如关系 employee 的模式包含一个属性 children,children 是一系列名字的集合。所以是非原子的。
- 组合属性 是非原子的:比如地址 address 属性,包含子属性 street、city、state 和 zip。所以是非原子的。
第二范式 2NF(Second Normal Form)
要求:
在第一范式的基础上:每个非主属性 完全函数依赖于 整个 主键:
- 每个非主属性必须被 整个主键 定义,不能被部分主键定义
- 没有部分函数依赖
温习:
- 完全函数依赖:若X->Y,并且对于X的任何一个真子集X',都有Y不函数依赖于X',则称Y对X完全函数依赖。
- 部分依赖例子:通过(学生学号,课程号)可以得到(该生姓名),而通过单独的(学生学号)已经能够得到(该生姓名),则说明(该生姓名)部分依赖于(学生学号,课程号);
- 部分依赖会造成数据冗余及各种异常。
第二范式分解的例子:
第三范式 3NF(Third Normal Form)
要求:
在第二范式的基础上:没有传递依赖
- 传递依赖定义:如果X->Y,Y不是X的子集且Y不函数依赖于X;Y->Z,Z不是Y的子集且Z不依赖于Y。X->Z 称Z对X传递函数依赖。
- 例如:在关系R(学号,宿舍,费用)中,通过(学号)可以得到(宿舍),通过(宿舍)可以得到(费用),而反之都不成立,则存在传递依赖(学号)->(费用)。
- 传递依赖也会造成数据冗余及各种异常。
第三范式分解的例子:
BCNF(Boyce-Codd Normal Form)
要求:
具有函数依赖 F 的关系模式 R 属于 BCNF 的条件是,对 F+ 中所有形如 的函数依赖,下面至少有一项成立:
- α—>β 是平凡函数依赖(即 β⊆α)
- α 是模式 R 的一个超码
总结就是 每个非平凡函数依赖的 左边都是超码
例子:
R = {A, B, C}
F = {A—>B, B—>C}
Key = {A}
- R 不是 BCNF
- 将 R 分解为 R1 = (A, B) ,R2 = (B, C)
- R1 和 R2 是 BCNF
- 无损分解,依赖保持
检验是否为 BCNF:
检验一个非平凡函数依赖 α—>β 是否违反了 BCNF:
- 计算 α 的闭包 α+ 是否包含 R 中的所有属性,如果包含,那么 α 就是一个超码。α—>β 就没有违反 BCNF
- 反之,这个是 非平凡函数依赖 且 α 不是超妈,一个条件都不满足。违反了 BCNF
按上面的方式 检验 F 中的所有非平凡函数依赖,如果都没有违反 BCNF,那么就是 R 满足 BCNF 的
不用检查 F+ 中所有函数依赖,可以证明,F 中没有函数依赖违反 BCNF,F+ 中也不会有函数依赖违反 BCNF
BCNF 分解算法
result := {R}
done := false
计算 F+(还是 Fc ?)
while (not done) do
if (result 中存在设计模式 R,不属于 BCNF)
then begin
令 α—>β 为一个在 Ri 上成立的非平凡函数依赖,满足 α 的闭包不是超码(违反了 BCNF)
result := (result - Ri) ∪ (Ri - β) ∪ (α, β);
end
else done := true;
例子
让我们考虑一个关系表 CTHRSG ,这里 C=course,T=teacher,H=hour,R=room,S=Student,G=grade。函数依赖集 F 我们假设为:
- CS —> G 每个学生的每门课有一个成绩
- C—>T 每门课有一个老师
- HR—>C 一个教室同一时间只能有一门课在上
- HS—>R 一个学生同一时间只能在一个教室上课
- TH—>R 一个老师同一时间只能在一个教室上课
分解过程
从 BCNF 到 3NF
BCNF 有一些问题:
BCNF 不是保持依赖的。在分解不能保持依赖时,对函数依赖的检查可能会用到连接,例如 α—>β 分解后,如果没有一个关系同时包含 α, β,就需要将包含它们的两个关系连接起来,再将 α, β 投影出来。
解决:
定义一个稍弱的函数依赖,叫做第三范式:
- 允许一些冗余
- 函数依赖检查 可以在 独立的关系上,而不用计算连接
- 总是无损分解,保持依赖的
第三范式定义:
具有函数依赖 F 的关系模式 R 属于 BCNF 的条件是,对 F+ 中所有形如 的函数依赖,下面至少有一项成立:
- α—>β 是平凡函数依赖(即 β⊆α)
- α 是模式 R 的一个超码
- β - α 中的每个属性 A 都包含于 R 的一个候选码中(每个属性可以包含于不同的候选码中)
第三个条件是确保 依赖保持的 对 BCNF 的最小程度的放松。
3NF 但不是 BCNF 的例子:
- R(S, T, J)
- F(SJ—>T, ST—>J, T—>J)
- 候选码:SJ,ST(属性闭包都是 STJ)
T—>J 不满足 BCNF 的条件:非平凡,T 也不是候选码
但是满足 3NF,即 J 包含于候选码 SJ 中
选 SJ 当主键更好,因为 Fc={SJ—>T, T—>J},这样 {S, J, T},T 完全依赖于主键
3NF 分解方法:
- 计算正则覆盖 Fc
- 计算候选码
- 对于 Fc 中的每一个函数依赖 α—>β,生成关系 Ri = αβ
- 如果上一步生成的所有模式中都不包含 R 的候选码,则将候选码也生成一个 R
- 重复 在上面生成的所有 R 中,如果某个 R 是另一个 R 的子集就去掉,直到不再有包含关系
3NF 分解例1:
R = {A, B, C, D, E}
F = {AB—>CDE, AC—>BDE, B—>C, C—>D, B—>E}
R 是几 NF ? 将 R 分解为 3NF,并且这个分解是保持依赖和无损分解
- 正则覆盖 Fc = {AC—>B, B—>C, C—>D, B—>E}
- 寻找候选码 XLN = A, YLR = BC,推出候选码为 {AB、AC}
- 判断是几 NF
- 对于 C—>D ,它不是平凡函数依赖,C 不是超码,所以 R 不是 BCNF。又 D 也不包含于任何一个候选码中,所以 R 不是 3NF。
- D 部分依赖于主键 AC,所以 R 不是 2NF。所以 R 是 1NF。
- 按照 Fc 中的函数依赖,将 R 分解为
- U1 = {A, B, C}, F1={AC—>B, B—>C}(BC 是 ACB 的子集,所以合并了)
- U2 = {B, C, E}, F2={B—>C, B—>E}(左边相同的依赖合并)
- U3 = {C, D}, F3={C—>D}
- 结果为 {R1<U1, F1>, R2<U2, F2>, R3<U3, F3>},这个分解是依赖保持的
- 候选码AC、AB 都在 U1 中,所以是无损分解
3NF 分解例2:
R = {A, B, C, D}
F = {A—>C, C—>A, B—>AC, D—>AC, BD—>A}
R 是几 NF ? 将 R 分解为 3NF,并且这个分解是保持依赖和无损分解
- 正则覆盖 Fc = {A—>C, C—>A, B—>A, D—>A}
- 寻找候选码 XLN = BD, YLR = AC,推出候选码为 {BD}
- 判断是几 NF
- 对于 B—>A 和 D—>A ,它们不是平凡函数依赖,B 或 D 不是超码,所以 R 不是 BCNF。又 A 也不包含于任何一个候选码中,所以 R 不是 3NF。
- A 部分依赖于主键 BD,所以 R 不是 2NF。所以 R 是 1NF。
- 按照 Fc 中的函数依赖,将 R 分解为
- U1 = {A, C}, F1={A—>C, C—>A}
- U2 = {A, B}, F2={B—>A}
- U3 = {A, D}, F3={D—>A}
- 结果为 {R1<U1, F1>, R2<U2, F2>, R3<U3, F3>},这个分解是依赖保持的
- 但是候选码 BD 不在任何关系中,所以将 R4<{B, D}, > 加到结果中。这个结果是无损分解
设计目标
比较 BCNF 和 3NF
将一个关系进行 3NF 分解,那么分解总是可以:
- 无损分解的
- 依赖保持的
将一个关系进行 BCNF 分解,那么分解是:
- 无损分解的
- 但可能不会依赖保持
但是 3NF 可能会造成一些冗余,比如下面这个例子:
R=(J, K, L)
F={JK—>L, L—>K}
- Fc = F ,候选码是 JK
- 对于平凡函数依赖 L—>K ,L 不是超码,所以不是 BCNF
- 又 K 包含于候选码 JK,所以是 3NF
- 如果分解为 BCNF 应该是两个关系 R1={JL} R2={LK}(先把不符合的 L—>K 以及 K 分离出去),这样不是依赖保持的(JK—>L 不保持)
- 如果分解为 3NF,按函数依赖来分,分为两个:JKL 和 LK,LK 包含于 JKL 于是合并,所以最后是 JKL 这样一个关系。
- 但从下面的表格中我们可以看出 L—>K 这个依赖在表中是有一些冗余的
J | L | K |
j1 | l1 | k1 |
j2 | l1 | k1 |
j3 | l1 | k1 |
null | l2 | k2 |
设计目标
关系型数据库的设计目标是:
- BCNF
- 无损连接
- 依赖保持
如果我们不能完成它,我们接受以下之一:
- 依赖保持的缺失
- 因为使用 3NF 造成的冗余
第四范式
多值依赖
- 函数依赖 规定如果 α—>β 成立,那么一个 α 对应唯一一个 β,不能有两个元组在 α 上的值相同而在 β 上的值不同。
- 多值依赖 α —>—> β 是说一个 α 的值可以对应多个 β。但是 α 仍然决定 β,也就是说 α 与 β 之间的联系独立于 α 与 R-α-β 之间的联系。
- 如果 α—>β,那么一定有 α—>—>β
多值依赖的例子:
R1(教师ID,教师名字) R2(教师ID,系,地址)
- R1 上有一个函数依赖 教师ID—>教师名字,R1 是满足 BCNF 的
- R2 是满足 BCNF 的,但一个教师可以在多个系,一个系有多个教师,所以 教师ID—>系,系 —>教师ID 都不成立
- R2 上仍然存在冗余,一个教师的 多个系 和 多个地址 要交叉连接
从多值依赖来考虑
- 一个教师可以有多个系,教师ID —>—> 系 是一个多值依赖
- 一个教师可以有多个地址,教师ID —>—> 地址 是一个多值依赖
显然,根据多值依赖将 R2 分解为 (教师ID,系)(教师ID,地址)更好
第四范式 4NF 的定义:
4NF 是在 BCNF 的基础上剔除了多值依赖。4NF 模式一定属于 BCNF。
函数依赖和多值依赖集为 D 的关系模式 r(R) 属于第四范式(4NF)的条件是,对 D+ 中所有形如 α—>—>β 的多值依赖,至少有以下成立:
- α—>—>β 是一个平凡的多值依赖
- α 是 R 的一个超码
更多的范式
多值依赖有助于我们理解并消除某些形式的信息重复,而这种信息重复用函数依赖是无法理解的。还有一些类型的约束称作连接依赖,它概化了多值依赖,并引出了另一种范式称作 投影-连接 范式(Project - Join Normal Form, PJNF)(PJNF 在某些书里称为第五范式)
还有一类更一般化的约束,它引出一种称作 域-码 范式(Domain - Key Normal Form,DKNF)的范式。