Skip to content

函数依赖与关系模式设计

函数依赖基础

函数依赖的定义

函数依赖是关系数据库设计中描述属性之间约束的核心概念:

  • 函数依赖:如果关系 R 中任意两个元组在属性集 X 上取值相同,则它们在属性集 Y 上的取值也必须相同,记为 XY

    • X 称为决定因素(或左部),Y 称为依赖因素(或右部)
    • X 函数决定 Y,或 Y 函数依赖于 X
    • 例如:name → addr favBeer 表示姓名决定了地址和喜欢的啤酒
  • 逻辑蕴含:如果关系 R(U,F) 的任意关系实例 r 都满足函数依赖 XY,则称函数依赖集 F 逻辑蕴含函数依赖 XY,记为 FXY

Armstrong公理系统

Armstrong公理系统是函数依赖的基本推理规则,可以用来推导函数依赖的所有逻辑结果:

  1. 自反律(Reflexivity)

    • 如果 YXU,则 XY 为 F 所蕴含
    • 直观理解:一个属性集总是能决定它的子集
  2. 增广律(Augmentation)

    • 如果 XY 为 F 所蕴含,且 ZU,则 XZYZ 为 F 所蕴含
    • 直观理解:如果X决定Y,那么在X和Y两边添加相同的属性Z后,关系仍然成立
  3. 传递律(Transitivity)

    • 如果 XYYZ 为 F 所蕴含,则 XZ 为 F 所蕴含
    • 直观理解:依赖关系具有传递性

函数依赖的推理规则

基于Armstrong公理系统,可以推导出以下常用的函数依赖推理规则:

  1. 合并规则

    • 如果 XYXZ,则 XYZ
    • 例如:已知 ABAC,可推导 ABC
  2. 分解规则

    • 如果 XYZ,则 XYXZ
    • 例如:已知 ABC,可推导 ABAC
  3. 伪传递规则

    • 如果 XYWYZ,则 XWZ
    • 例如:已知 ABCBD,可推导 ACD

闭包计算

属性闭包

  • 属性闭包:给定函数依赖集F和属性集X,X关于F的属性闭包(记为 XF+)是X在F下能决定的所有属性的集合。
  • 用途
    • 判断函数依赖 XY 是否被F逻辑蕴含(检查Y是否包含在XF+中)
    • 判断属性集X是否为关系模式的超键(检查XF+是否等于U)
    • 判断属性集X是否为关系模式的候选码(检查X是否为极小的使XF+ = U的属性集)

函数依赖闭包

  • 函数依赖闭包:给定函数依赖集F,F的闭包(记为F+)是F所逻辑蕴含的所有函数依赖的集合。
  • 计算方法:通过Armstrong公理系统从F出发推导所有可能的函数依赖。
  • 性质
    • 如果XYF+,则FXY
    • 两个函数依赖集F和G是等价的,当且仅当F+=G+

闭包计算算法

属性闭包的计算算法是函数依赖理论的核心:

属性闭包计算算法

  1. 初始化:令 X(0)=X
  2. 迭代扩展:对于每一步i,找到所有满足 VWFVX(i) 的函数依赖,将W中不在X(i)中的属性添加到X(i)中,得到X(i+1)
  3. 终止条件:当X(i+1)=X(i)时停止,此时X(i)即为XF+

示例: 计算 (AB)F+,其中 F={ABC,BD,CE,ECB,ACB}

  1. 初始:X(0)=AB
  2. 第一次迭代:
    • ABC:加入C,得到ABC
    • BD:加入D,得到ABCD
  3. 第二次迭代:
    • CE:加入E,得到ABCDE
  4. 第三次迭代:没有新属性加入,停止
  5. 结果:(AB)F+=ABCDE

最小函数依赖集

最小依赖集的定义

最小函数依赖集(也称为极小覆盖或规范覆盖)是一个等价于原函数依赖集F的简化版本,满足以下条件:

  1. 每个函数依赖的右部仅包含单个属性
  2. 无法移除任何函数依赖而保持等价性
  3. 无法将任何函数依赖的左部替换为其真子集而保持等价性

意义:最小函数依赖集消除了冗余,是设计关系模式和判断范式的基础。

最小依赖集的计算步骤

计算最小函数依赖集的步骤如下:

  1. 右部单一化

    • 将每个函数依赖 XY1Y2...Yn 拆分为一组函数依赖 XY1,XY2,...,XYn
  2. 去除冗余依赖

    • 对于每个函数依赖 XA,检查是否可以从F中移除而不改变F+
    • 具体方法:计算X在F{XA}下的属性闭包,如果闭包包含A,则该依赖是冗余的
  3. 左部简化

    • 对于每个函数依赖 XA,检查是否可以用X的真子集Z替代X
    • 具体方法:对于X中的每个属性B,计算(X{B})在F中的属性闭包,如果闭包包含A,则B是冗余的

计算示例

计算F={ABC,BD,CE,ECB,ACB}的最小函数依赖集:

  1. 右部单一化:F已经满足右部单一化

  2. 去除冗余依赖

    • 检查 ABC:在F{ABC}下,(AB)F{ABC}+=ABD,不包含C,所以ABC不是冗余的
    • 检查 BD(B)F{BD}+=B,不包含D,所以BD不是冗余的
    • 检查 CE(C)F{CE}+=C,不包含E,所以CE不是冗余的
    • 检查 ECB(EC)F{ECB}+=ECB,包含B,所以ECB是冗余的,移除
    • 检查 ACB(AC)F{ACB}+=ACEB,包含B,所以ACB是冗余的,移除
  3. 左部简化

    • 对于 ABC

      • 检查A:(B)F+=BD,不包含C,所以A不是冗余的
      • 检查B:(A)F+=A,不包含C,所以B不是冗余的
    • 对于 BD:B是单属性,无法简化

    • 对于 CE:C是单属性,无法简化

  4. 最终结果Fm={ABC,BD,CE}

关系模式的分解与范式

候选码与超键

  • 超键(Superkey):关系模式R中的属性集K,若K函数决定R的所有属性,则K为R的超键。
  • 候选码(Candidate Key):R的极小超键,即不包含任何真子集是超键的超键。
  • 主码(Primary Key):从候选码中指定的一个,用作关系的唯一标识。
  • 主属性(Prime Attribute):包含在任一候选码中的属性。
  • 非主属性(Non-prime Attribute):不包含在任何候选码中的属性。

范式层次

关系模式的范式层次描述了关系模式对函数依赖的符合程度:

  1. 第一范式(1NF)

    • 关系的每个属性都是原子的(不可再分)
    • 所有关系数据库都满足1NF
  2. 第二范式(2NF)

    • 满足1NF
    • 每个非主属性完全函数依赖于每个候选码
    • 消除了部分依赖
  3. 第三范式(3NF)

    • 满足2NF
    • 每个非主属性不传递依赖于任何候选码
    • 消除了传递依赖
  4. BC范式(BCNF)

    • 对于每个非平凡函数依赖XY,X都是超键
    • BCNF比3NF更严格,消除了所有基于函数依赖的冗余

模式分解

模式分解是将一个关系模式分解为若干个更小的关系模式,以消除冗余和异常:

分解为BCNF的步骤

  1. 找到违反BCNF的函数依赖XY(X不是超键)
  2. 计算XF+
  3. 将R分解为R1=XF+R2=X(RXF+)
  4. 递归应用于R1R2,直到所有关系都满足BCNF

示例: 分解R=ABCDEF={ABC,CDE,BD,EA}

  1. 检查ABC:A不是超键(AF+=ABCDE但A不是极小的)
  2. AF+=ABCDE
  3. 分解为R1=ABCDER2=A,但R2已包含在R1
  4. 检查CDE:CD不是超键
  5. (CD)F+=CDEABC=ABCDE
  6. 分解为R1=CDER2=CD(ABCDECDE)=ABCD
  7. 继续递归检查...

3NF合成算法: 当BCNF分解可能导致函数依赖无法保持时,可使用3NF合成算法:

  1. 找到F的最小覆盖Fm
  2. Fm中每个函数依赖XA创建一个关系模式Ri=XA
  3. 如果没有任何模式包含候选码,则添加一个只包含候选码的关系模式
  4. 如果某个关系模式被其他关系模式完全包含,则可以去除