关系模式分解是数据库系统工程师和软件设计师每年必考的核心考点,其中无损连接分解的判定、依赖保持性的检验,以及两者之间的区别,是命题人最偏爱挖坑的地方。不少考生在考场上面对"该分解是否为无损连接分解"的选择题时犹豫不决,原因在于只记住了结论,没有真正理解表格法与二分解定理背后的逻辑。本文将从概念定义出发,深挖无损分解的底层原理与判定算法,系统梳理分类、常见误区与历年真题,帮助读者彻底拿下这一高频丢分点。
关系模式分解,是指把一个关系模式及其上的函数依赖集,拆分为若干个更小的关系模式的集合。设关系模式 R 的属性集为 U,其上的函数依赖集为 F,将 R 分解为若干子关系模式 R1、R2、直至 Rn,记作分解 ρ,即 ρ 等于 R1、R2 到 Rn 的集合。分解的根本目的,是消除原关系模式中存在的插入异常、删除异常、数据冗余和更新异常,使模式达到更高的规范化程度。
所谓无损连接分解,又称无损分解,是指关系模式 R 被分解为多个子模式之后,将这些子模式上的关系实例通过自然连接运算重新连接起来,所得结果与原关系实例完全一致,不丢失任何信息,也不产生任何多余的信息。从形式化的角度看,设 R 的一个关系实例为 r,其分解后各子模式上的投影分别为 r1、r2、直至 rn,那么当且仅当 r 等于各投影的自然连接,即 r 等于 r1 自然连接 r2 自然连接直至 rn 时,称该分解相对于函数依赖集 F 是无损连接分解。
与无损分解相对的是有损连接分解。有损分解意味着分解后的各子模式经过自然连接之后,得到的结果中出现了原关系中不存在的元组,这些多出来的元组被称为悬浮元组或伪元组。伪元组的出现使得连接结果无法准确还原原始关系,说明分解过程中丢失了信息。需要注意的是,这里所说的"信息"并非指数据本身被物理删除,而是指属性之间的关联约束被破坏了,导致在还原时无法唯一确定原始状态。
与无损分解并列的另一个重要概念是依赖保持分解。设分解 ρ 将 R 分解为 R1 到 Rn,若 F 在每个子模式上的投影的并集,其闭包恰好等于 F 的闭包,则称该分解是保持函数依赖的分解。简单地说,依赖保持分解要求原关系模式上所有函数依赖所表达的数据约束,都能在分解后的某个子模式上被完整地体现和约束,而不会因为分解而丢失任何一条函数依赖。无损分解关注的是数据能否被完整还原,依赖保持分解关注的则是语义约束能否被完整保留,两者是两个相互独立、不可混淆的目标。
对于最常见的二分解情形,存在一个简洁且必须牢记的充要条件定理。设关系模式 R 分解为两个子模式 R1 和 R2,则 ρ 等于 R1、R2 是 R 关于函数依赖集 F 的无损连接分解,当且仅当 R1 与 R2 的交集,能够函数决定 R1 减去 R2 的差集,或者 R1 与 R2 的交集能够函数决定 R2 减去 R1 的差集。用形式化表达即为,R1 交 R2 函数决定 R1 减 R2 属于 F 的闭包,或者 R1 交 R2 函数决定 R2 减 R1 属于 F 的闭包。这个定理是考试中判断二分解是否无损的最快工具,它的成立基于一个深刻的直觉:两个子模式的公共属性若能决定其中一侧的独有属性,则自然连接时不会产生歧义,信息可以无损还原。
判断一个分解是否保持函数依赖,需要逐条检验原函数依赖集中的每一条依赖是否被分解后的子模式集合所蕴涵。具体做法是,对于 F 中的每一条函数依赖 X 函数决定 Y,考察 X 与 Y 的并集是否被包含在某个子模式 Ri 的属性集当中。若是,则这条依赖自然被该子模式保留;若否,则需要通过求 X 关于 F 的投影并集的闭包,判断 Y 是否被该闭包包含。若全部函数依赖都能被保留,则该分解是保持函数依赖的分解。
理解无损分解,必须先弄清楚有损分解中伪元组产生的根本原因。自然连接是一种基于公共属性相等的连接运算,它的本质是将两个关系中在公共属性上取值相同的元组拼接起来。当原关系被分解时,属性被分散到不同的子模式中,原本由同一元组承载的、跨越多个子模式的属性关联,只能依靠公共属性来维系。一旦公共属性不足以唯一确定某侧的全部属性,那么在自然连接还原时,就会出现"张冠李戴"式的错误配对。
举例而言,设关系模式 R 包含三个属性 A、B、C,函数依赖集 F 中只有一条依赖 A 函数决定 B。若将 R 分解为 R1 等于 AB 和 R2 等于 AC,则公共属性是 A。由于 A 函数决定 B 成立,根据二分解定理,A 即 R1 交 R2,能够函数决定 B 即 R1 减 R2 的结果,因此该分解是无损的。反之,若将 R 分解为 R1 等于 AB 和 R2 等于 BC,公共属性是 B,而 B 并不能函数决定 A,也不能函数决定 C,则分解是有损的。原因在于,B 的相同取值下,A 与 C 之间没有任何约束关系,多个 A 值与多个 C 值可以任意两两组合,自然连接时便会生成大量原关系不存在的元组。
这个例子揭示了一个核心机制:无损分解的关键,在于公共属性集合必须是至少一侧子模式的候选码或超码,使其能够充当"连接键",把分散的属性重新准确配对。当公共属性能够函数决定一侧的全部剩余属性时,另一侧的元组与公共属性是一对一或一对多的确定关系,连接不会产生歧义;当公共属性不足以决定任何一侧时,两侧元组在公共属性上形成多对多的关系,连接必然产生笛卡尔积式的膨胀,这就是有损分解的底层逻辑。
当分解的子模式超过两个,即属于多分解情形时,二分解定理不再直接适用,此时需要借助表格法进行判定。表格法又称追踪法或 Chase 算法,其核心思想是构造一个二维矩阵,然后反复利用函数依赖集中各依赖对矩阵进行修改填充,最终通过检查矩阵中是否存在一行全部为可区分的原始符号来判断分解是否无损。
表格法的执行步骤如下。第一步,构造一个 n 行 m 列的矩阵,其中 n 等于子模式的个数,m 等于原关系模式属性集 U 中的属性个数,每一列对应一个属性,每一行对应一个子模式。第二步,逐行初始化矩阵:对于第 i 行第 j 列,若属性 Aj 属于子模式 Ri,则填入可区分符号 aj,即 a 加下标 j;若属性 Aj 不属于子模式 Ri,则填入不可区分符号 bij,即 b 加双下标 i 和 j。第三步,扫描函数依赖集 F 中的每一条依赖 X 函数决定 Y,若存在两行或多行在 X 所对应的列上取值完全相同,则将这些行在 Y 所对应的列上统一修改为相同取值,优先统一为可区分符号 aj,若无法统一为 aj,则统一为某一行的 bij。第四步,重复第三步,直到矩阵不再发生任何变化为止。最终,若矩阵中存在至少一行全部为 a 类可区分符号,则该分解是无损连接分解;若没有任何一行全为 a,则该分解是有损连接分解。
表格法的巧妙之处在于,它把"能否还原"这一语义问题,转化为一个机械的、可执行的符号传播过程。函数依赖相当于一条条传播规则,当某几行在决定属性上一致时,它们在被决定属性上也必须一致,这一过程不断把不可区分的 bij 符号替换为可区分的 aj 符号,本质上是在模拟自然连接后属性值的逐步确定过程。若能最终确定出完整的一行,说明连接结果能够唯一还原出原始元组,分解无损。
依赖保持分解的判定,其底层逻辑与无损分解不同。函数依赖表达的是一种语义层面的数据完整性约束,例如"学号函数决定姓名"意味着每个学号只能对应唯一的姓名。当关系模式被分解后,如果某条函数依赖所涉及的属性被拆分到不同的子模式中,那么这条约束就无法在任何一个单一子模式上被强制维护,只能依靠应用程序在多个子模式之间进行跨表校验,这既容易出错,也违背了数据库系统应当内建约束的理念。因此,保持函数依赖的分解要求在分解时尽量把同一函数依赖的属性归入同一个子模式,从而让约束能够由数据库系统自动维护。
关系模式分解可以从多个维度进行分类。按子模式的数量,分为二分解与多分解,二分解只有两个子模式,可直接套用充要条件定理,多分解则需使用表格法。按分解的性质,分为无损连接分解与有损连接分解。按是否保持依赖,分为保持函数依赖的分解与不保持函数依赖的分解。值得注意的是,无损连接性与依赖保持性是两个正交的性质,一个分解既可能是无损且保持依赖的,也可能是无损但不保持依赖的,也可能是保持依赖但有损的,最差的则是有损且不保持依赖。
在实际的规范化设计中,不同的目标对应不同的分解要求。若目标是达到第三范式,即 3NF,则存在同时满足无损连接且保持函数依赖的分解算法,能够做到两个性质兼得。若目标上升到 BCNF,即鲍依斯-科得范式,则由于 BCNF 本身是一个比 3NF 更严格的约束
本篇完!