文章目录
- 数据库模式分解:如何判定"保持函数依赖"?
-
- 一、先搞懂:什么叫"保持函数依赖"?
- 二、判定保持函数依赖的标准思路
-
- [第一步:列出原函数依赖集 F F F](#第一步:列出原函数依赖集 F F F)
- 第二步:对每个子模式求"投影依赖"
- 第三步:合并所有子模式的依赖集
- [第四步:检查 F F F 中每个依赖是否都能被 G G G 推导出来](#第四步:检查 F F F 中每个依赖是否都能被 G G G 推导出来)
- 三、考试常用"快速判断法"
- 四、严格判定:用"跨子模式闭包扩展"算法
- 五、用一个例子串起来
-
- [1. 看每个依赖落在哪里](#1. 看每个依赖落在哪里)
- [2. 合并依赖集](#2. 合并依赖集)
- [3. 判定结果](#3. 判定结果)
- 六、容易踩的三个坑
-
- [坑 1:把"保持函数依赖"当成"无损连接"](#坑 1:把“保持函数依赖”当成“无损连接”)
- [坑 2:依赖被拆开就直接判"不保持"](#坑 2:依赖被拆开就直接判“不保持”)
- [坑 3:只检查"直接出现",不做闭包推导](#坑 3:只检查“直接出现”,不做闭包推导)
- 七、做题模板
数据库模式分解:如何判定"保持函数依赖"?
关系模式分解里,有两个最容易混的概念:
- 无损连接:拆完表后,能不能通过自然连接把数据完整拼回去。
- 保持函数依赖:拆完表后,原来的业务约束是否还能被"找回来"。
这篇只讲后者:怎么判定一个分解是否保持函数依赖。
一、先搞懂:什么叫"保持函数依赖"?
设关系模式是:
R ( U , F ) R(U, F) R(U,F)
其中:
- U U U 是属性集;
- F F F 是原函数依赖集;
- 把 R R R 分解成若干子模式: R 1 , R 2 , ... , R k R_1, R_2, \dots, R_k R1,R2,...,Rk。
如果 F F F 中的每个函数依赖 ,都能由分解后各子模式上的函数依赖推导出来,那么这个分解就是保持函数依赖的。
通俗一点说:
原来的规则,拆表之后不能丢。
要么某个依赖完整地落在某张子表里;
要么虽然被拆开了,但通过各子表的依赖联合推导,仍然能恢复出来。
注意:保持函数依赖 ≠ 无损连接 。
一个分解可以保持依赖但不是无损连接;也可以无损连接但不保持依赖。
二、判定保持函数依赖的标准思路
判定流程可以分成四步。
第一步:列出原函数依赖集 F F F
先把题目给出的所有函数依赖写清楚,例如:
F = { A → B , A → C , C → D , A E → G } F = \{A \rightarrow B,\ A \rightarrow C,\ C \rightarrow D,\ AE \rightarrow G\} F={A→B, A→C, C→D, AE→G}
后面每一步都要围绕这个集合检查。
第二步:对每个子模式求"投影依赖"
对每个子模式 R i R_i Ri,找出那些左右属性都落在 R i R_i Ri 中的函数依赖。
例如:
- 若 R 1 ( A , B , C , D ) R_1(A, B, C, D) R1(A,B,C,D),
- 原依赖中有 A → B A \rightarrow B A→B、 A → C A \rightarrow C A→C、 C → D C \rightarrow D C→D,
- 那么这些依赖都可以在 R 1 R_1 R1 上保持。
如果某个依赖的左边或右边属性不在 R i R_i Ri 中,它就不能直接归入 R i R_i Ri 的投影依赖集。
第三步:合并所有子模式的依赖集
把所有子模式能"认领"的依赖合并起来,记为:
G = ⋃ i = 1 k F i G = \bigcup_{i=1}^{k} F_i G=i=1⋃kFi
其中 F i F_i Fi 是 F F F 在 R i R_i Ri 上的投影依赖集。
第四步:检查 F F F 中每个依赖是否都能被 G G G 推导出来
对 F F F 中的每个依赖 X → Y X \rightarrow Y X→Y,判断:
Y ⊆ X G + Y \subseteq X_G^+ Y⊆XG+
也就是说,用合并后的依赖集 G G G 计算 X X X 的闭包,看能否推出 Y Y Y。
如果 所有依赖都能被推出 ,则分解保持函数依赖;
只要有一个推不出,就不保持。
三、考试常用"快速判断法"
做选择题时,通常可以先用这个简化判断:
如果一个函数依赖的左右属性都完整出现在某个子模式中,那它一定被保持。
如果某个依赖被拆到不同子模式里,且无法通过其他依赖推导出来,那它就可能丢失。
但要注意:
被拆开 ≠ 一定不保持 。
因为可能存在传递推导。例如:
- R 1 ( A B ) R_1(AB) R1(AB) 上有 A → B A \rightarrow B A→B;
- R 2 ( B C ) R_2(BC) R2(BC) 上有 B → C B \rightarrow C B→C;
- 那么 A → C A \rightarrow C A→C 虽然不在任何单张子表中,但可以通过 A → B A \rightarrow B A→B、 B → C B \rightarrow C B→C 推导出来,因此仍可能被保持。
所以严格判定还是要看闭包。
四、严格判定:用"跨子模式闭包扩展"算法
当某个依赖被拆散时,不能直接判"不保持",需要用更通用的方法。
对 F F F 中的每个依赖 X → Y X \rightarrow Y X→Y:
-
初始化:
r e s u l t = X result = X result=X
-
对每个子模式 R i R_i Ri:
- 计算:
t e m p = r e s u l t ∩ R i temp = result \cap R_i temp=result∩Ri - 求 t e m p temp temp 关于原依赖集 F F F 的闭包:
t e m p + temp^+ temp+ - 取回落在 R i R_i Ri 内的属性:
t = t e m p + ∩ R i t = temp^+ \cap R_i t=temp+∩Ri - 更新:
r e s u l t = r e s u l t ∪ t result = result \cup t result=result∪t
- 计算:
-
重复第 2 步,直到 r e s u l t result result 不再变化。
-
最后判断:
如果 Y ⊆ r e s u l t Y \subseteq result Y⊆result,则 X → Y X \rightarrow Y X→Y 被保持;否则不保持。
这个算法的本质是:
利用每个子模式内部能推出的属性,逐步扩大左部能决定的范围,看最终能不能覆盖右部。
五、用一个例子串起来
设:
U = { A , B , C , D , E , G } U = \{A, B, C, D, E, G\} U={A,B,C,D,E,G}
F = { A → B , A → C , C → D , A E → G } F = \{A \rightarrow B,\ A \rightarrow C,\ C \rightarrow D,\ AE \rightarrow G\} F={A→B, A→C, C→D, AE→G}
分解为:
- R 1 ( A , B , C , D ) R_1(A, B, C, D) R1(A,B,C,D)
- R 2 ( A , E , G ) R_2(A, E, G) R2(A,E,G)
1. 看每个依赖落在哪里
- A → B A \rightarrow B A→B:属性都在 R 1 R_1 R1,保持。
- A → C A \rightarrow C A→C:属性都在 R 1 R_1 R1,保持。
- C → D C \rightarrow D C→D:属性都在 R 1 R_1 R1,保持。
- A E → G AE \rightarrow G AE→G:属性都在 R 2 R_2 R2,保持。
2. 合并依赖集
合并后得到:
G = { A → B , A → C , C → D , A E → G } G = \{A \rightarrow B,\ A \rightarrow C,\ C \rightarrow D,\ AE \rightarrow G\} G={A→B, A→C, C→D, AE→G}
这和原依赖集完全一致。
3. 判定结果
因为 F F F 中每个依赖都能由 G G G 直接推出,所以该分解保持函数依赖。
六、容易踩的三个坑
坑 1:把"保持函数依赖"当成"无损连接"
保持依赖看的是规则有没有丢 ;
无损连接看的是数据能不能拼回去 。
两者要分别判定。
坑 2:依赖被拆开就直接判"不保持"
比如 A → C A \rightarrow C A→C 没有直接出现在任何子表中,但如果子表中有:
- A → B A \rightarrow B A→B
- B → C B \rightarrow C B→C
那么 A → C A \rightarrow C A→C 仍可能被推导出来。
坑 3:只检查"直接出现",不做闭包推导
严格做法是:
对每个 X → Y X \rightarrow Y X→Y,计算 X X X 在合并依赖集下的闭包,看是否包含 Y Y Y。
七、做题模板
以后遇到"是否保持函数依赖"的题,可以按这个顺序写:
- 写出原函数依赖集 F F F。
- 对每个子模式 R i R_i Ri,写出它能保持的依赖 F i F_i Fi。
- 合并得到 G = ⋃ F i G = \bigcup F_i G=⋃Fi。
- 对 F F F 中每个 X → Y X \rightarrow Y X→Y,判断 Y ⊆ X G + Y \subseteq X_G^+ Y⊆XG+。
- 全部成立 → 保持函数依赖;
有一个不成立 → 不保持函数依赖。
如果是选择题,通常可以先用"左右属性是否完整落在某个子模式中"快速排除;遇到跨表依赖,再用闭包扩展法确认。