关系数据理论
问题的提出:为何需要关系数据理论?
在设计关系数据库时,我们常常面临选择:如何将现实世界的信息组织成合适的关系模式?同一个系统可以有多种设计方案,但不同的方案优劣差异很大。
核心问题:
- 如何评价一个关系模式设计的好坏?
- 如何设计出性能良好的关系模式?
「关系数据库的规范化理论」正是为了解决这两个问题而提出的。一个「好」的关系模式应该:
- 减少数据冗余:尽可能少地存储重复信息。
- 避免操作异常:避免在进行插入、删除、更新数据时出现问题。
不良设计的后果
如果关系模式设计不当,通常会导致以下几类问题,统称为操作异常(Anomalies):
- 数据冗余(Redundancy)
- 更新异常(Update Anomaly)
- 插入异常(Insertion Anomaly)
- 删除异常(Deletion Anomaly)
不良模式设计实例分析
我们通过一个简化的学生选课场景来理解这些问题。假设有以下属性:
Sno
:学号Sdept
:学生所在系Mname
:系主任姓名Cno
:课程号Grade
:成绩
现实语义约束:
- 一个学生只属于一个系。
- 一个系有多名学生。
- 一个系只有一名正职系主任。
- 一个学生可选修多门课程,一门课程可被多名学生选修。
- 一个学生修读一门课程只有一个成绩。
设计方案 1:单一关系模式
将所有属性放入一个关系模式 Student(Sno, Sdept, Mname, Cno, Grade)
。
根据语义,我们有以下函数依赖(后续会详细定义):
Sno -> Sdept
:学号确定系Sdept -> Mname
:系确定系主任(Sno, Cno) -> Grade
:学号和课程号确定成绩
该方案存在的问题:
- 数据冗余:
- 学生每次选课,其
Sdept
和Mname
都会被存储一次。如果一个学生选了 10 门课,他的系和系主任信息就重复存储了 10 次。 - 同一个系的所有学生的
Mname
信息都会重复。 - 这不仅浪费存储空间,更重要的是为数据不一致埋下隐患。
- 学生每次选课,其
- 更新异常:
- 如果某系更换了系主任,需要修改该系所有学生所有选课记录中的
Mname
值。如果遗漏了某些记录,就会导致数据不一致。
- 如果某系更换了系主任,需要修改该系所有学生所有选课记录中的
- 插入异常:
- 如果一个系(如新成立的「信息安全系」,系主任「王必成」)刚成立,还没有学生选课,我们无法将这个系及其主任的信息存入数据库。因为
(Sno, Cno)
是主码或候选码的一部分,不能为空,而此时没有Sno
和Cno
。 - 同样,如果一个学生刚入学,尚未选任何课程,也无法单独存储该学生的学号和系信息(如果
(Sno, Cno)
是主码)。
- 如果一个系(如新成立的「信息安全系」,系主任「王必成」)刚成立,还没有学生选课,我们无法将这个系及其主任的信息存入数据库。因为
- 删除异常:
- 如果某个学生(如
S8
)只选修了一门课,并且他是其所在系(如「人工智能」)的最后一名学生。当他毕业或退学,删除其选课记录时,可能会导致「人工智能」这个系及其系主任的信息从数据库中丢失。
- 如果某个学生(如
设计方案 2:多个关系模式
将 Student
模式分解为三个关系模式:
S(Sno, Sdept)
:学生信息,FD:Sno -> Sdept
DEPT(Sdept, Mname)
:系信息,FD:Sdept -> Mname
SC(Sno, Cno, Grade)
:选课信息,FD:(Sno, Cno) -> Grade
该方案的优点:
- 数据冗余减少:系主任姓名
Mname
只在DEPT
表中存储一次。学生所在的系Sdept
也只在S
表中存储一次。 - 更新异常解决:更换系主任只需修改
DEPT
表中对应的一条记录。 - 插入异常解决:可以独立地在
DEPT
表中添加新系的信息,也可以在S
表中添加新学生的信息,无需等待选课。 - 删除异常解决:删除
SC
表中的选课记录不会影响S
表中的学生信息或DEPT
表中的系信息。删除S
表中的学生信息也不会影响DEPT
表。
结论
方案 2 明显优于方案 1。产生这些差异的根本原因在于关系模式内部属性之间的依赖关系处理不当。
数据依赖
数据依赖(Data Dependency)是关系内部属性与属性之间的一种约束关系,是现实世界属性间相互联系的抽象,是数据内在的性质,体现了语义。
- 主要类型包括:函数依赖(Functional Dependency, FD)和多值依赖(Multi-Valued Dependency, MVD)。
- 不好的模式设计往往源于存在不合适的数据依赖。
核心思想:
规范化理论的目标就是通过模式分解(Schema Decomposition),将一个低级范式的关系模式转换为若干个高级范式的关系模式集合,以此来消除不合适的数据依赖,减少冗余和操作异常。
函数依赖
函数依赖
设 是一个属性集 上的关系模式, 是 的子集。若对于 的任意一个可能的关系 , 中不可能存在两个元组在 上的属性值相等,而在 上的属性值不等,则称 函数确定 或 函数依赖于 ,记作 。
- 称为这个函数依赖的决定因素(Determinant)。
- 称为这个函数依赖的依赖因素(Dependent)。
理解要点:
- 语义层面:FD 是基于现实世界语义的约束,不是单从某个数据库快照(关系实例)推导出来的。例如,「学号确定姓名」(
Sno -> Sname
)是因为现实世界规定一个学号对应一个学生。 - 实例的作用:关系实例(表中的数据)不能证明一个 FD 存在,但可以证伪一个 FD。如果在实例中发现存在 值相同但 值不同的元组,则 必然不成立。
- 所有实例:一个 FD 成立,意味着它必须在关系模式 的 所有 可能的关系实例上都得到满足。
- 表示:
- 如果 且 ,则记为 。
- 如果 不函数依赖于 ,则记为 。
图形化理解:想象一个二维坐标系, 轴代表属性(集) 的值域, 轴代表属性(集) 的值域。
- 如果 成立,那么对于任意一个 值,最多只能对应一个 值(类似数学函数的定义)。
- 如果 ,那么至少存在一个 值,对应了多个不同的 值。
函数依赖的类型
非平凡函数依赖 vs. 平凡函数依赖
- 非平凡 FD:若 ,且 ,则为非平凡 FD。例如
Sno -> Sdept
。 - 平凡 FD:若 ,且 ,则为平凡 FD。例如
(Sno, Cno) -> Sno
。
平凡 FD 总是成立,没有实际意义。我们通常关注和讨论的是「非平凡 FD」。
完全函数依赖 vs. 部分函数依赖
设 中有 ,且 是属性组合。
- 完全函数依赖(Fully Functional Dependency):如果对于 的任何真子集 ,都有 ,则称 对 完全函数依赖。记作 。
- 例:在
SC(Sno, Cno, Grade)
中,(Sno, Cno) -F> Grade
(假设成绩不由学号或课程号单独决定)。
- 例:在
- 部分函数依赖(Partial Functional Dependency):如果存在 的一个真子集 ,使得 ,则称 对 部分函数依赖。记作 。
- 例:在
SLC(Sno, Sdept, Sloc, Cno, Grade)
中,键为(Sno, Cno)
。因为Sno -> Sdept
,所以Sdept
部分依赖于(Sno, Cno)
,即(Sno, Cno) -P> Sdept
。
- 例:在
传递函数依赖
设 中有 。
如果 (Y 不能反过来决定 X),且 (Z 不被 Y 包含,即 是非平凡的),则称 对 传递函数依赖。记为 。
例如在 Student(Sno, Sdept, Mname, ...)
中,Sno -> Sdept
, Sdept -> Mname
。假设 Sdept -/> Sno
(一个系有多个学生),且 Mname
不是 Sdept
的一部分。则 Mname
传递依赖于 Sno
。
为何区分依赖类型?
部分函数依赖和传递函数依赖是导致关系模式冗余和操作异常的主要原因之一。规范化过程很大程度上就是为了消除这些不良依赖。
码
码是关系数据库中用于唯一标识元组并建立关系间联系的关键概念,其定义与函数依赖密切相关。
码与相关概念
设关系模式为 ,其中 为属性集, 为函数依赖集。设 。
- 超码:如果 (即 能函数确定 中的所有属性),则 是 的一个超码。
- 候选码:如果 是一个超码,并且对于 的任何真子集 , 都不是超码(即 是最小的超码),则 是 的一个候选码,简称码。
- 主码:如果一个关系模式有多个候选码,则选定其中一个作为主码。主码的选择通常基于简洁性、稳定性等因素。
- 全码:如果关系模式的所有属性构成其唯一的候选码,则称该码为全码。
- 主属性:包含在任何一个候选码中的属性。
- 非主属性或非码属性:不包含在任何候选码中的属性。
- 外码:关系模式 中的属性(或属性组) 不是 的码,但 是另一个关系模式 的主码(或候选码),则称 是 的外码。外码用于建立和强制关系之间的联系(参照完整性)。
示例:
S(Sno, Sdept)
:Sno
是候选码(也是主码)。Sno
是主属性,Sdept
是非主属性。SC(Sno, Cno, Grade)
:(Sno, Cno)
是候选码(也是主码)。Sno
,Cno
是主属性,Grade
是非主属性。DEPT(Sdept, Mname)
:Sdept
是候选码(也是主码)。Sdept
是主属性,Mname
是非主属性。- 在
SC
中,Sno
可以是参照S
表主码Sno
的外码。 Student(Sno, Sname, Ssex, Sage, Sdept)
,如果规定Sno
唯一,Sname
也唯一(不允许重名)。- 候选码:
Sno
,Sname
。 - 可选
Sno
为主码。 - 主属性:
Sno
,Sname
。 - 非主属性:
Ssex
,Sage
,Sdept
。
- 候选码:
规范化
规范化是利用形式化的方法(基于函数依赖等),将关系模式分解,使其达到某种「良好」的标准(范式),从而减少数据冗余和操作异常的过程。
范式(Normal Form, NF)
范式是衡量关系模式「良好」程度的标准。不同级别的范式对模式中允许存在的函数依赖有不同的要求。
主要范式(基于 FD):
- 第一范式(1NF):关系模式 的所有属性都是原子的(不可再分的)。这是关系模型的基本要求。
- 第二范式(2NF):在 1NF 基础上,消除非主属性对候选码的部分函数依赖。
- 第三范式(3NF):在 2NF 基础上,消除非主属性对候选码的传递函数依赖。
- BCNF(Boyce-Codd Normal Form):在 3NF 基础上,消除任何属性(包括主属性)对码的部分依赖和传递依赖。更严格地说,要求所有函数依赖的决定因素必须包含候选码(即决定因素必须是超码)。
范式间的关系:
- 满足高一级范式的关系模式必定满足低一级范式。
- 满足低一级范式的关系模式不一定满足高一级范式。
规范化过程:通过模式分解(将一个关系模式分解为多个关系模式),逐步将关系模式从低级范式提升到高级范式。
第一范式(1NF)
定义
如果一个关系模式 的所有属性域都是原子的,即每个属性值都是不可再分的基本数据项,则称 属于第一范式。
1NF 是关系模型的最低要求,非 1NF 的数据库模式不能称为关系数据库。
满足 1NF 只是基础,并不能保证模式是「好」的,如前面分析的 Student(Sno, Sdept, Mname, Cno, Grade)
就满足 1NF,但存在严重问题。
第二范式(2NF)
定义
若关系模式 ,并且每一个非主属性都完全函数依赖于 的任何一个候选码,则称 。
核心要求:消除非主属性对码的部分依赖。
检查步骤:
- 找出 的所有候选码。
- 找出 的所有非主属性。
- 检查每个非主属性是否对每个候选码都存在部分依赖。如果存在任何一个非主属性对任何一个候选码的部分依赖,则 。
示例:关系模式 SLC(Sno, Sdept, Sloc, Cno, Grade)
,候选码为 (Sno, Cno)
。
- 非主属性:
Sdept
,Sloc
,Grade
。 - 函数依赖:
(Sno, Cno) -> Grade
,Sno -> Sdept
,Sdept -> Sloc
(假设学生住处由所在系决定,即一个系的学生住一起),Sno -> Sloc
(由前两个传递得到)。 - 检查:
Grade
完全依赖于(Sno, Cno)
(假设)。Sdept
依赖于Sno
,而Sno
是(Sno, Cno)
的真子集,所以Sdept
部分依赖于(Sno, Cno)
。Sloc
依赖于Sno
,所以Sloc
部分依赖于(Sno, Cno)
。
- 结论:
SLC
不属于 2NF。
不满足 2NF 会导致由部分依赖引起的冗余和操作异常(如 SLC
例子中 Sdept
, Sloc
的冗余存储、更新/插入/删除异常)。
- 解决方法:投影分解。将
SLC
分解为:SC(Sno, Cno, Grade)
:候选码(Sno, Cno)
,非主属性Grade
完全依赖于码,满足 2NFSL(Sno, Sdept, Sloc)
:候选码Sno
,非主属性Sdept
,Sloc
完全依赖于码,满足 2NF
2NF 的局限性
分解到 2NF 后,虽然消除了非主属性对码的部分依赖,但仍可能存在非主属性对码的传递依赖,导致问题。例如,分解后的 SL
仍可能存在冗余(如果 Sdept -> Sloc
)。
第三范式(3NF)
定义
设关系模式 。若 中不存在这样的候选码 、属性组 及非主属性 (),使得 成立,且 (即 不是超码),则称 。
核心要求:消除非主属性对码的传递依赖。
- 等价地说:如果 ,且没有任何非主属性传递依赖于任何候选码,则 。
- 更简洁的判断(对 2NF 模式):检查是否存在非主属性 通过另一个(或一组)非主属性 传递依赖于码 。
检查步骤(基于 2NF):
- 确认 。
- 找出 的所有候选码和非主属性。
- 检查是否存在非主属性 传递依赖于某个候选码 (即存在 ,其中 本身不是超码,且 是非主属性)。如果存在,则 。
示例:上面得到的关系模式 SL(Sno, Sdept, Sloc)
,候选码 Sno
。
- 非主属性:
Sdept
,Sloc
。 - 函数依赖:
Sno -> Sdept
,Sdept -> Sloc
。假设Sdept -/> Sno
。 - 检查:
Sno -> Sdept
,Sdept -> Sloc
,且Sdept
不是候选码(超码),Sloc
是非主属性。存在非主属性Sloc
对码Sno
的传递依赖。 - 结论:
SL
不属于 3NF。
不满足 3NF 会导致由传递依赖引起的冗余和操作异常。
- 解决方法:投影分解。将
SL
分解为:SD(Sno, Sdept)
:候选码Sno
,满足 3NFDL(Sdept, Sloc)
:候选码Sdept
,满足 3NF
此时,原 SLC
模式最终分解为 SC(Sno, Cno, Grade)
, SD(Sno, Sdept)
, DL(Sdept, Sloc)
。这三个模式都达到了 3NF。
3NF 的改进
达到 3NF 后,基本消除了由非主属性引起的部分依赖和传递依赖,大大改善了模式质量。但在某些特殊情况下,3NF 仍可能存在冗余和异常,特别是当依赖关系涉及到主属性时。
BCNF(Boyce-Codd Normal Form)
BCNF 是修正的第三范式,提出了更强的约束。
定义
设关系模式 。若对于 中每个非平凡的函数依赖 (其中 ), 都必定包含 的某个候选码(即 必须是 的一个超码),则称 。
核心要求:任何函数依赖的决定因素都必须是超码。
与 3NF 的关系:
- BCNF 必定是 3NF。
- 3NF 不一定是 BCNF。
- 区别在于:3NF 允许非主属性被非超码的属性(组)决定(只要不是传递依赖),而 BCNF 要求任何属性(包括主属性)都不能被非超码的属性(组)决定。
导致 3NF 但非 BCNF 的情况通常发生在:
- 存在多个候选码。
- 这些候选码有重叠属性。
- 存在一个函数依赖,其决定因素是候选码的一部分(但不是超码),且依赖因素也是主属性。
示例 1:仓库管理
关系模式 Warehouse(仓库名, 管理员, 物品名, 数量)
- 语义:
- 一个仓库可有多个管理员,一个管理员只在一个仓库工作(
管理员 -> 仓库名
)。 - 一个仓库可存放多种物品,一种物品可存放于多个仓库。
- 对于一个仓库中的特定物品,由唯一指定的管理员负责,且有确定数量(
(仓库名, 物品名) -> 管理员
,(仓库名, 物品名) -> 数量
)。
- 一个仓库可有多个管理员,一个管理员只在一个仓库工作(
- 函数依赖:
管理员 -> 仓库名
,(仓库名, 物品名) -> 管理员
,(仓库名, 物品名) -> 数量
。 - 候选码:
(仓库名, 物品名)
和(管理员, 物品名)
。 - 主属性:
仓库名
,管理员
,物品名
。非主属性:数量
。 - 检查 3NF:
数量
完全依赖于两个候选码,没有部分依赖或传递依赖。满足 3NF。
- 检查 BCNF:
- FD
管理员 -> 仓库名
。决定因素管理员
不是超码(它不能决定所有属性,如物品名
和数量
)。
- FD
- 结论:
Warehouse
属于 3NF,但不属于 BCNF。
仍存在冗余(管理员和仓库名的配对可能重复)和操作异常(如更换仓库管理员可能需要修改多条记录)。
- 解决方法:分解为 BCNF。
R1(管理员, 仓库名)
:FD:管理员 -> 仓库名
,码管理员
,满足 BCNFR2(管理员, 物品名, 数量)
:FD:(管理员, 物品名) -> 数量
,码(管理员, 物品名)
,满足 BCNF
示例 2:学生-教师-课程
关系模式 STJ(S, T, J)
(S: 学生, T: 教师, J: 课程)
- 语义:
- 每个教师只教一门课(
T -> J
)。 - 每门课有若干教师。
- 一个学生选修一门课,有唯一对应的教师(
(S, J) -> T
)。 - 一个学生跟一个老师,也确定了课程(
(S, T) -> J
,由前两个 FD 可推导)。
- 每个教师只教一门课(
- 函数依赖:
T -> J
,(S, J) -> T
。 - 候选码:
(S, J)
和(S, T)
。 - 主属性:
S
,T
,J
。没有非主属性。 - 检查 3NF:由于没有非主属性,自动满足 3NF。
- 检查 BCNF:
- FD
T -> J
。决定因素T
不是超码(不能决定S
)。
- FD
- 结论:
STJ
属于 3NF,但不属于 BCNF。
解决方法:分解为 BCNF。
TJ(T, J)
:FD:T -> J
,码T
,满足 BCNFST(S, T)
:无非平凡 FD,码(S, T)
,满足 BCNF
BCNF 的意义
如果一个关系数据库中的所有关系模式都达到了 BCNF,那么在函数依赖的范畴内,可以说模式已经实现了彻底的分解,达到了最高的规范化程度,从理论上消除了由函数依赖引起的插入和删除异常以及更新异常。
Armstrong 公理系统与函数依赖理论
在关系数据库理论中,函数依赖的推理是进行模式分析和优化的基础。Armstrong 公理系统(Armstrong's Axiom System)提供了一套形式化的规则,用于从已知的函数依赖集合中推导出所有逻辑上被蕴涵的函数依赖。
逻辑蕴涵与函数依赖集的闭包
逻辑蕴涵
设 是关系模式 上的一个函数依赖集。如果对于 的任何一个满足 的关系实例 ,函数依赖 也都在 中成立,则称 逻辑蕴涵 ,记作 。
- 这意味着,只要 中的所有函数依赖都得到满足,那么 也必然得到满足。
- 「 逻辑蕴涵 」 也可以描述为「 可以从 出发根据 Armstrong 公理推导得到」。
函数依赖集的闭包()
对于给定的函数依赖集 ,所有被 逻辑蕴涵的函数依赖的集合称为 的闭包(closure of F),记为 。
计算 本身可能非常复杂(NP 问题),因为它可能包含大量的函数依赖。
Armstrong 公理系统
Armstrong 公理系统[1]包含三条基本规则和若干条可以由基本规则推导出来的扩充规则。该系统是有效且完备的,这意味着:
- 有效性(sound):任何从 使用 Armstrong 公理推导出的函数依赖 ,都必然满足 (即 )。
- 完备性(complete):任何满足 的函数依赖(即 ),都可以从 使用 Armstrong 公理推导出来。
基本推理规则
设关系模式为 , 是 的子集。
-
A1 自反律:若 ,则 被 所蕴涵。
- 解释:如果属性集 是属性集 的一部分,那么 的值自然能确定 的值。这通常产生平凡函数依赖。自反律的使用不依赖于 中已有的函数依赖。
- 证明:对 的任一关系 中的任意两个元组 ,若 。因为 ,所以 必然等于 。故 成立。
-
A2 增广律:若 且 ,则 。 ( 表示 )
- 解释:如果在 的基础上增加一些属性 作为决定因素,那么在 的基础上对应增加相同的属性 后,依赖关系依然成立。
- 证明:设 为 所蕴涵。对任意元组 ,若 ,则 且 。因为 ,所以 。因此 。故 成立。
-
A3 传递律:若 且 ,则 。
- 解释:函数依赖关系可以像链条一样传递。
- 证明:设 和 为 所蕴涵。对任意元组 ,若 。因为 ,所以 。又因为 ,所以 。故 成立。
扩充推理规则
由基本规则可以推导出以下常用的扩充规则:
- 合并规则:若 且 ,则 。
- 分解规则:若 且 ,则 。
- 证明:
- (已知)
- (因 ,由 A1 自反律)
- (由 1, 2 和 A3 传递律)
- 证明:
- 伪传递规则:若 且 ,则 。
- 证明:
- (已知)
- (已知)
- (由 1 和 A2 增广律,用 增广 )
- (由 3, 2 和 A3 传递律)
- 证明:
成立的充分必要条件是 对所有的 都成立。
- 该引理可以由合并规则(证明 方向)和分解规则(证明 方向)得到。
属性集的闭包
直接计算 是困难的。一个更实用的方法是计算属性集 关于 的闭包,记为 (或简写为 当 明确时)。
属性集闭包()
在关系模式 中,属性集 关于函数依赖集 的闭包 是指所有能被 通过 和 Armstrong 公理函数确定的属性的集合。
等价地,。
成立的充分必要条件是 。
这意味着,要判断 是否函数确定 ,我们只需要计算 ,然后检查 是否是 的子集。
计算属性集闭包的算法
- 输入:属性集 ,函数依赖集 。
- 输出:。
- 初始化:令 。令 。
- 循环计算:。
- 更简洁的写法:
- 终止条件:如果 或者 ,则 ,算法终止。
- 否则,令 ,返回步骤 2。
属性集闭包计算示例
已知关系模式 ,其中 ,
。求 。
- :
- 且 ,将 加入。。
- 且 (因为 ),将 加入。。
- 。
- :
- 且 ,将 加入。。
- ,算法终止。
所以,。
函数依赖集的覆盖与等价
函数依赖集的覆盖与等价
- 覆盖(Cover):如果函数依赖集 能够逻辑蕴涵函数依赖集 中的所有函数依赖(即 ),则称 覆盖 。
- 等价(Equivalent):如果两个函数依赖集 和 的闭包相等(即 ),则称 与 等价,记为 。
的充分必要条件是 覆盖 且 覆盖 (即 且 )。
最小函数依赖集(最小覆盖)
为了简化函数依赖集并进行有效的模式分解,我们通常需要找到一个与原函数依赖集等价的、且尽可能简化的版本,称为最小函数依赖集或最小覆盖。
最小函数依赖集
一个函数依赖集 是最小的,如果它满足以下三个条件:
- 右部单一性: 中每个函数依赖 的右部 都只包含一个属性。即形如 。
- 左部无冗余属性: 中不存在函数依赖 ,使得 中有某个真子集 满足 。也就是说,对于 中的任一 ,不存在 的真子集 使得 能由 导出(或者更准确地说,不能用 替换 而保持等价性,除非 本身是多余的)。这意味着 的每个属性都是必需的。
- 无冗余依赖: 中不存在函数依赖 ,使得 。也就是说,每个函数依赖都是必需的。
条件 1(右部单一性)主要是为了方便后续条件的检查。条件 2 确保决定因素中没有不必要的属性。条件 3 确保整个函数依赖本身不是由集合中其他依赖推导出来的。一个给定的 可能有多个不等价的最小覆盖。
计算最小覆盖的算法
给定函数依赖集 ,计算其最小覆盖 的步骤如下:
- 分解右部:将 中所有形如 的函数依赖替换为一组函数依赖 。得到 。
- 消除左部冗余属性:对 中的每个函数依赖 和 中的每个属性 :
计算 (即在当前整个 集合下计算属性闭包)。如果 ,则用 替换 中的 。此步骤可能需要重复进行,直到没有左部冗余属性。 - 消除冗余依赖:对处理后的 中的每个函数依赖 :
计算 (即在去掉当前 FD 后,用剩余 FD 计算闭包)。如果 ,则从 中移除 。
最终得到的 就是一个最小覆盖 。
最小覆盖计算
设 。
- 分解右部:(已满足)。
- 消除左部冗余属性:
- 考虑 。
- 去掉 ,剩 。计算 :
- (无变化)
- 。
- 因为 ,所以 中 是冗余的。用 替换 。
- 。
- 如果先去掉 ,剩 。计算 。因为 ,所以 中 是冗余的。用 替换 。得到 。
- 我们选择 继续。
- 去掉 ,剩 。计算 :
- 考虑 。
- 消除冗余依赖(基于 ):
- 考虑 。去掉它,用 。计算 。因为 ,所以 不冗余。
- 考虑 。去掉它,用 。计算 。因为 ,所以 不冗余。
- 考虑 。去掉它,用 。计算 。因为 ,所以 不冗余。
最终最小覆盖 。
如果步骤 2 得到 ,类似地会发现它也是最小的。这说明最小覆盖可能不唯一。
模式分解
规范化理论的核心思想是通过模式分解将一个关系模式分解为若干个更小、结构更好的关系模式的集合,以消除数据冗余和操作异常。
关系模式的分解
关系模式 的一个分解 是指一组关系模式的集合 ,其中:
- (所有子模式的属性并集等于原模式的属性集)。
- 通常不要求 ,即子模式间可以有共同属性。
- 是 在 上的投影,即 。这意味着 包含了 中所有仅涉及 内属性的(直接或间接)函数依赖。
一个好的模式分解应具备两个主要特性:无损连接性和依赖保持性。
无损连接性
无损连接性确保在分解后,可以通过自然连接操作从子关系中恢复出原始关系的所有信息,而不会产生额外(伪)元组或丢失原有元组。
无损连接
设 是 的一个分解。如果对于 的任何关系实例 ,都有 成立,则称分解 具有无损连接性。
- 其中 是 在属性集 上的投影。
- 表示自然连接。
- 如果分解是「有损」的,则 ,这意味着连接操作会产生一些不属于原始关系的「伪元组」。
- 无损连接是模式分解的首要和必须满足的准则。
无损连接的判定(二元分解)
定理
对于关系模式 的分解 ,该分解具有无损连接性的充分必要条件是:
或者 。
- 是 和 的公共属性。
- 是只在 中出现的属性。
- 是只在 中出现的属性。
直观上,公共属性必须能函数确定至少一个子模式中的非公共属性。
无损连接判断
设关系模式 。
- 分解为 和 。。
若 ,则 成立,分解是无损的。
若 ,则 成立,分解是无损的。 - 分解为 和 。。
若 ,则 且 。分解是有损的。
若 ,则 成立,分解是无损的。
对于分解为多个关系模式的情况,可以迭代使用此定理,或者使用更通用的 Chase 算法(或称为表方法)来判定。
依赖保持性
依赖保持性确保原关系模式中的所有函数依赖在分解后的子模式中仍然能够被强制执行或推导出来。
依赖保持
设 是 的一个分解,其中 是 在 上的投影。如果 ,则称分解 保持函数依赖。
- 由于 总是成立的,所以我们只需检验是否 。
- 这意味着,原有的每个函数依赖 都能通过子模式的函数依赖集合推导出来。
模式分解算法
候选码的计算
在进行模式分解和范式判断前,经常需要计算关系模式的候选码。一种常用的方法是基于属性集的分类:
- 设 是关系模式 的一个最小函数依赖集。
- 将 中的属性分为四类:
- :仅出现在 中函数依赖左部的属性。
- :仅出现在 中函数依赖右部的属性。
- :同时出现在 中函数依赖左部和右部的属性。
- :未在 中任何函数依赖的左部或右部出现的属性。
- 初步判断:
- 所有在 中的属性必须是任何候选码的成员。令 。
- 仅在 中的属性不能是任何候选码的成员(除非 本身是 的一部分且 为空)。
- 组合测试:计算 。如果 ,则 是唯一候选码。
否则,从 中选择属性子集 ,逐个加入 形成 ,测试是否 。寻找满足此条件的最小 。
候选码计算
条件:
- 。
步骤:
- (没有只在左边的)
- (C 只在右边)
- (A, B 左右都出现)
- 。
- 从 中选子集:
- 测试 :,所以 是一个候选码。
- 测试 :,所以 是一个候选码。
- 候选码为 和 。
转换为 3NF 的无损且保依赖分解算法
该算法旨在将一个关系模式分解为一组满足 3NF 的子模式,同时保证分解是无损连接且保持函数依赖的。
- 计算最小覆盖:找出 的一个最小覆盖 。
- 按依赖分组:对于 中的每一个函数依赖 (由于是最小覆盖,右部已是单属性),创建一个关系模式 。分解 初始化为这些 的集合。
- 保证无损连接:检查 中的关系模式是否包含原模式 的某个候选码。
- 如果没有任何一个 使得 是 的一个超码(即包含 的一个候选码),则从 的候选码中任选一个 ,将 添加到 中。
- 否则,无需操作。
- (可选)可以合并具有相同候选码或者一个模式的属性集是另一个模式属性集子集的模式,以减少模式数量,但需注意合并后仍需满足 3NF。
SLC 分解到 3NF
R = SLC(Sno, Sdept, Sloc, Cno, Grade)
Fmin = {(Sno, Cno) -> Grade, Sno -> Sdept, Sdept -> Sloc}
- 候选码为
(Sno, Cno)
。
Fmin
已给出。- 根据
Fmin
创建子模式:R1(Sno, Cno, Grade)
(来自(Sno, Cno) -> Grade
)R2(Sno, Sdept)
(来自Sno -> Sdept
)R3(Sdept, Sloc)
(来自Sdept -> Sloc
)rho = {R1, R2, R3}
。
- 检查候选码:
R1
的属性集{Sno, Cno, Grade}
包含了原模式的候选码(Sno, Cno)
。因此无需添加候选码模式。
最终分解为:
SC(Sno, Cno, Grade)
SD(Sno, Sdept)
DL(Sdept, Sloc)
这个分解是无损的,保持依赖的,并且每个子模式都达到了 BCNF(因此也是 3NF)。
转换为 BCNF 的无损分解算法
该算法旨在将一个关系模式分解为一组满足 BCNF 的子模式,保证分解是无损连接的,但不一定保持函数依赖。
- 初始化:。
- 循环检查与分解:
- 如果 中的所有关系模式都已达到 BCNF,则算法终止。
- 否则,选择 中某个不满足 BCNF 的关系模式 。
- 这意味着在 中存在一个函数依赖 () 使得 不是 的超码。
- 将 分解为两个子模式:
- (其中 是 在 上的投影)
- (其中 是 在 上的投影)
- 注意: 应该理解为 或者 ,即 仍保留在第二个模式中以保证无损连接,因为 且 。
- 更准确的分解是: 和 。
- 用 替换 中的 。返回步骤 2。
STJ 分解到 BCNF
R = STJ(S, T, J)
(学生 S, 教师 T, 课程 J)F = {(S, J) -> T, T -> J}
。候选码为(S, J)
和(S, T)
。STJ
是 3NF(因为所有属性都是主属性),但不是 BCNF,因为T -> J
中T
不是STJ
的超码。
rho = {STJ(S, T, J)}
。STJ
不满足 BCNF,因为T -> J
且T
不是超码。- 分解
STJ
基于T -> J
:R1(T, J)
(属性:T, J
。FD:T -> J
)R2(S, T)
(属性:S, T
。原F
在ST
上的投影不含非平凡 FD,但(S, J) -> T
意味着S, T
共同决定J
, 结合T -> J
。- 这里 是
(S, T, J) - J = (S, T)
。所以是R2(S, T)
。 - 更准确地,分解为
R1(T, J)
和R2(S, T)
。
- 这里 是
rho = {R1(T, J), R2(S, T)}
。
算法终止。最终分解为 TJ(T, J)
和 ST(S, T)
。
这个分解是无损的,但原 FD (S, J) -> T
丢失了,因为无法从 F_TJ = {T -> J}
和 F_ST = {}
中推导出。
多值依赖与第四范式(4NF)
即使关系模式达到了 BCNF,仍可能存在由多值依赖引起的冗余。
多值依赖(MVD)
设 是一个关系模式, 是 的子集,且 (即 是 的一个划分,允许为空)。如果对于 的任一关系实例 ,给定一对 值( 是 上的值, 是 上的值), 的值的集合完全由 决定,而与 的值无关,则称 多值依赖于 ,或 多值决定 ,记作 。
等价定义: 成立,当且仅当对 的任一关系 ,若元组 和 均在 中,则元组 和 也必定在 中。
1 | X | Y | Z |
课程-教师-参考书
考虑关系模式 Teaching(Course, Teacher, Book)
。假设一门课程有多位教师授课,使用多本参考书,且每位教师都使用该课程的所有参考书,每本参考书也适用于该课程的所有教师。
- 例如,物理课由李勇、王军教,参考书有《普通物理学》《光学原理》。
- 则存在元组:
(物理, 李勇, 普物)
,(物理, 李勇, 光学)
,(物理, 王军, 普物)
,(物理, 王军, 光学)
。 - 这里,对于给定的课程(如「物理」),教师集合(
{李勇, 王军}
)的确定与参考书无关;同样,参考书集合({普物, 光学}
)的确定也与教师无关。 - 因此,存在多值依赖:
Course ->> Teacher
和Course ->> Book
。 - 该模式的唯一候选码是
(Course, Teacher, Book)
(全码),因此它属于 BCNF。但仍存在明显的冗余。
多值依赖的性质
- 平凡 MVD:若 或 ,则 是平凡的。我们关心的是非平凡 MVD。
- 函数依赖是特殊的多值依赖:若 ,则 。
- MVD 补余规则:若 且 ,则 。
- MVD 的有效性依赖于属性全集 ,而 FD 不依赖。
第四范式(4NF)
第四范式旨在消除由非平凡且非 FD 的 MVD 引起的冗余。
第四范式(4NF)
关系模式 ( 包含 FD 和 MVD)属于 4NF,如果对于 上的每一个非平凡多值依赖 (即 且 ), 都必须是 的一个超码。
- 如果一个关系模式是 4NF,则它必定是 BCNF。
- 4NF 限制关系模式的属性之间不允许有非平凡且非函数依赖的多值依赖。4NF 所允许的非平凡多值依赖实际上是函数依赖。
4NF 的分解
如果关系模式 不属于 4NF,则存在某个非平凡 MVD 且 不是 的超码。可以将 分解为:
- (或者 ,其中 )
这个分解是无损的,并且有助于消除冗余,使子模式更接近或达到 4NF。重复此过程直到所有模式都达到 4NF。
Teaching 模式分解到 4NF
Teaching(Course, Teacher, Book)
存在非平凡 MVD Course ->> Teacher
,且 Course
不是超码。分解为:
CT(Course, Teacher)
CB(Course, Book)
(因为U - Y = Book
)
这两个子模式 CT
和 CB
都达到了 4NF(它们只包含平凡 MVD 或由码决定的 MVD)。
通过 Armstrong 公理、属性闭包、最小覆盖等工具,我们可以分析函数依赖。通过模式分解,特别是针对 3NF、BCNF 和 4NF 的分解算法,我们可以设计出结构良好、冗余较少、不易发生操作异常的关系数据库模式。
数学基础中的「公理」(如欧几里得几何)被视为不证自明、无需证明的起点,其真值通过直觉或经验接受;逻辑与数据库理论中的「公理」更强调形式系统的初始规则,用于推导其他定理。这些规则可能是可证明的(例如从更基础的集合论出发),但在当前系统内被设定为不可约简的起点。 ↩︎