关系模式的分解,是软考数据库系统工程师、软件设计师乃至系统分析师考试中绕不开的一块硬骨头。很多考生能把范式背得滚瓜烂熟,张口就是第一范式到BCNF,可真到做题时,一遇到"该分解是否具有无损连接性""是否保持函数依赖""属于有损还是无损"这类问题就乱了阵脚。究其原因,在于范式只是规定了关系模式应当满足的约束条件,而真正把不规范的关系模式改造成规范关系模式的工具,恰恰是分解。分解看似只是把一个大表拆成几个小表,实则牵涉到两条必须同时权衡的判定标准,任何一条没吃透,都会被命题人精准拿捏。本文不满足于概念复述,而是要把分解背后的底层原理、判定算法、命题套路一次性讲透。
要理解分解,必须先回到一个更根本的问题:为什么关系模式需要被分解。关系数据库设计的核心目标,是构建一个既没有数据冗余、又不会在插入删除更新时产生异常的关系模式集合。然而现实中的关系模式,往往因为属性之间的函数依赖处理不当,而陷入冗余与异常的泥潭。规范化的本质,就是通过逐步消解关系中"不好"的函数依赖,把低范式关系提升到更高范式,而分解正是实现这种提升的唯一手段。
所谓关系模式的分解,指的是将一个关系模式按某种规则拆分为若干个子模式的过程。形式化地说,给定关系模式R及其函数依赖集F,若存在关系模式集合ρ={R1,R2,...,Rn},使得这些子模式并集恰好等于R,且彼此间没有完全相同的属性集合,则称ρ是R的一个分解。这里有个易被忽略的细节:分解不是任意切分属性,而是要求子模式合并起来能完整覆盖原关系的全部属性,不能多也不能少,这个约束构成了后续判定的物理基础。
分解之所以成为规范化的核心工具,是因为它能剥离造成冗余的"罪魁祸首"--部分函数依赖与传递函数依赖。一个同时包含学生信息与课程信息的关系,往往会因为存在"学号决定学生姓名、课程号决定课程名称"这样的部分依赖,导致同一学生姓名在多条选课记录里反复出现。通过分解把学生、课程信息分别放入独立子模式,冗余便随之消失。但这里引出了分解必须面对的第一重矛盾:分解之后的信息,还能不能无损地还原回分解之前。
无损连接分解,是关系模式分解的第一条判定标准。它的含义是:R分解为R1、R2后,将二者做自然连接,如果得到的结果与原来的R完全一致,既不多一条也不少一条,那么就是无损连接分解;反之,如果连接结果比原R多出记录,则是有损连接分解。
这里要强调一个表述上的微妙之处。无损所说的"无损",指的是信息内容的无损,而非元组数量的无损。有损连接的"有损",恰恰表现在分解再连接之后会产生原本不存在的虚假元组,也就是"连接陷阱"。这些多出来的记录,是信息被错误切分所导致的伪数据,会污染查询结果。因此,无损连接性是判断分解是否合格的底线标准,任何一个不具备无损连接性的分解都不可接受。
形式化地,设ρ={R1,R2}是R的分解,若对R上任意满足F的合法实例r,都有r等于r在R1上的投影与r在R2上的投影的自然连接,则称ρ相对于F是无损连接分解。定义的精髓在于"对任意合法实例都成立",即无损连接性是与函数依赖相关的全局性质,而非针对某份具体数据的偶然性质,这也解释了为何无损判定必须依赖函数依赖集。
保持函数依赖,是关系模式分解的第二条判定标准。它的含义是:R分解为若干子模式后,原函数依赖集F能够由分解后各子模式上的函数依赖投影所蕴含。换句话说,若F中的每条函数依赖,要么直接出现在某个子模式的依赖集中,要么能由这些子模式的依赖推导出来,则这个分解是保持函数依赖的。
保持函数依赖的意义在于维护数据库的完整性约束。函数依赖本质上是一种完整性约束,规定了属性间取值必须满足的语义关系。若分解后某条依赖无法在任何单个子模式中体现,数据库系统就不得不在多个子模式间做跨表连接才能验证它,既增加开销,也容易因操作疏漏导致约束失效。因此保持函数依赖的价值,在于让每条约束都能在分解后的某个局部关系中被就地检查。
需要特别澄清,无损连接与保持函数依赖是两个相互独立的标准,二者并不等价,也不存在必然蕴含关系。一个分解可以无损却不保依赖,也可以保依赖却有损。正是这种独立性,构成了命题人设计陷阱的天然土壤。
无损连接性的判定,是数据库方向的必考计算型考点。它的判定方法不止一种,但考试中最主流、最通用、也最容易被拿来设问的,是表格法,又称Chase算法。表格法的核心思想,是构造一张符号表模拟自然连接过程,观察这张表能否被"修复"出完整的一行,从而判断分解是否无损。
第一步构造初始表格。假设R被分解为R1、R2、Rn,表格行数等于子模式个数,列数等于原关系属性个数。每个格子填入符号:对于子模式Ri对应的行,若某列属性Aj属于Ri则填aj,否则填bij(i为行号,j为列号)。aj可理解为真实属性值,bij为未知的、待确定的属性值。这张初始表把每个子模式视为独立表,属于该子模式的属性有真实值,不属于的属性处于未知状态;自然连接的物理过程,本质上就是让这些未知值逐步被确定、让尽可能多的bij替换成aj。
构造好初始表格后,进入迭代阶段。迭代依据是原函数依赖集F。对F中每条函数依赖X→Y,检查表中是否存在两行在X对应各列取值完全相同。若存在,则让这两行在Y对应各列也保持一致。一致化操作是:对于Y中每个属性,若两行取值一个是aj、另一个是bij,则把bij改为aj;若两行都是未知符号bij和bkj,则让行号较小者覆盖较大者,或统一取其一为标准。
迭代需反复进行,直到表格不再变化为止。终止后检查每一行,若存在某行所有列都是aj形式、没有残留bij,则可判定分解无损;反之有损。关键在于:迭代本质是利用函数依赖去合并本应相同的属性值。一旦某行被完全修复成aj,就意味着通过自然连接,原本分散的信息被完整拼回一条原始记录,信息无丢失;反之若拼不出完整一行,说明必有信息在分解中丢失,连接会不可避免地产生虚假元组。
除通用表格法外,对最常见的二元分解,即把R分解为R1、R2的情形,还存在快速判定定理:二元分解ρ={R1,R2}无损的充要条件,是F的闭包中包含R1∩R2→R1或R1∩R2→R2中的任意一条。其中R1∩R2是两个子模式属性集合的交集。
这个定理的直觉很朴素:R1与R2做自然连接时,通过公共属性(交集)对接。若公共属性能决定其中一个子模式的全部属性,该子模式信息就由公共属性唯一确定,不产生歧义,故无损;反之若公共属性既不能决定R1也不能决定R2,连接时就因信息不足产生虚假元组,导致有损。该定理极大简化了二元分解的判定,考生只需算交集属性并验证其能否决定某一侧全部属性即可。
若说无损判定是第一道坎,保持函数依赖判定就是第二道坎,且更隐蔽、更易被忽视。很多考生做分解题只盯着无损看,把保依赖丢在一边,结果在"该分解是否保持函数依赖"的问题上栽跟头。
理解保依赖判定,先要理解函数依赖的投影。给定R上的函数依赖集F,及R的子模式Ri,F在Ri上的投影πRi(F),指的是F的闭包F+中所有满足"左右两边属性都完全包含在Ri中"的依赖所构成的集合。这里有两个要点:第一,投影计算针对闭包F+而非F本身,因为F中的某些依赖虽左右属性都落在Ri内,也可能存在某些由F推导出来、同样完全落在Ri内的依赖;第二,投影只保留属性完全被Ri覆盖的依赖。
投影概念回答的是:原关系上的约束在分解后,哪些仍能在某个子模式内部被就地表达。只有当一条依赖的左右属性恰好落在同一个子模式中,这条约束才可能被该子模式独立维护,因此投影正是对"分解后每个子模式各自能维护哪些约束"的精确刻画。
理解投影后,保依赖判定步骤就清晰了。第一步,计算F在每个子模式Ri上的投影πRi(F),将所有子模式投影取并集得到集合G。第二步,判断G能否覆盖F,即F中每条依赖是否都能由G逻辑蕴含。若F中每条依赖都能由G推导出来,则分解保依赖;若存在某条依赖无法由G推导,则分解不保依赖。
判断G是否覆盖F,常用属性闭包思路:对F中每条依赖X→Y,计算X在G下的属性闭包,若闭包包含Y则该依赖被G覆盖,反之丢失。该过程计算量不小,但逻辑链条完整自洽:投影描述子模式能维护的约束,并集汇总分解后全部可维护约束,覆盖判断检验这些约束是否足以还原原有全部约束。
无损与保依赖之间的独立性,是必须深入阐述的核心命题。理论上,一个分解可同时满足两者,也可只满足其一,甚至两者都不满足。这两条标准不存在谁包含谁的关系,是两个维度的、彼此正交的性质。
这种独立性带来的直接后果,是实际分解中常出现两难。规范化理论的一个经典结论是:任何关系模式都可分解到第三范式,且该分解能同时满足无损与保依赖;但若分解到BCNF,虽仍能保证无损,保依赖却未必能做到。也就是说,BCNF在消除更多冗余的同时,可能以牺牲保依赖为代价。这一结论是软考命题的重要来源,也提醒考生:范式高低与分解性质好坏并非简单正相关,BCNF并非在
本篇完!