数据库模式分解:如何判定“保持函数依赖”?

文章目录

数据库模式分解:如何判定"保持函数依赖"?

关系模式分解里,有两个最容易混的概念:

  • 无损连接:拆完表后,能不能通过自然连接把数据完整拼回去。
  • 保持函数依赖:拆完表后,原来的业务约束是否还能被"找回来"。

这篇只讲后者:怎么判定一个分解是否保持函数依赖。

一、先搞懂:什么叫"保持函数依赖"?

设关系模式是:

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:

  1. 初始化:

    r e s u l t = X result = X result=X

  2. 对每个子模式 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
  3. 重复第 2 步,直到 r e s u l t result result 不再变化。

  4. 最后判断:

    如果 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。

七、做题模板

以后遇到"是否保持函数依赖"的题,可以按这个顺序写:

  1. 写出原函数依赖集 F F F。
  2. 对每个子模式 R i R_i Ri,写出它能保持的依赖 F i F_i Fi。
  3. 合并得到 G = ⋃ F i G = \bigcup F_i G=⋃Fi。
  4. 对 F F F 中每个 X → Y X \rightarrow Y X→Y,判断 Y ⊆ X G + Y \subseteq X_G^+ Y⊆XG+。
  5. 全部成立 → 保持函数依赖;
    有一个不成立 → 不保持函数依赖。

如果是选择题,通常可以先用"左右属性是否完整落在某个子模式中"快速排除;遇到跨表依赖,再用闭包扩展法确认。

相关推荐
猫咪很可爱7 小时前
Oracle Data Pump 导入报错 ORA-02375:字符集不一致问题分析与处理
数据库·oracle
java1234_小锋7 小时前
MySQL 宣布正式拥抱 AI
数据库·人工智能·mysql
SabrinaW7117 小时前
基于 C++17 与 RTTR 的注册式对象工厂:配置驱动的动态对象创建
java·前端·数据库
NineData8 小时前
MySQL 迁移到 OceanBase 怎么做?NineData 全量迁移、CDC 实时同步与数据校验指南
数据库·mysql·oceanbase
standxy8 小时前
索引设计实战:pgvector HNSW × JSONB GIN × 复合索引
数据库·开发
guo_wen_qiang8 小时前
使用spring-data-mongodb操作mongo记录
数据库·mongodb
凤山老林8 小时前
授权模型只会RBAC?深入 ABAC 授权模型,实现权限判断做成策略引擎
大数据·数据库·算法
geovindu8 小时前
sql:SQL Views and Materialized Views using mysql 9.0
大数据·数据库·数据仓库·sql·mysql·数据库开发·数据库架构
梦影_8 小时前
MySQL进阶教程(二)—— 储存过程与触发器(下)
开发语言·数据库·后端·sql·mysql·oracle·数据库开发
工业涂料百问8 小时前
陶瓷涂料的光泽度怎么检测?20°、60°、85° 三个角度怎么选
数据库