形式陈述
从分解构造符号表
本页判定无损连接分解 公理库 无损连接分解 Lossless join decomposition · Dependency preservation 证明二元分解的公共属性闭包判据,并在同一四属性模式上比较无损、依赖保持、第三范式与 BCNF。 :输入有限属性集 U 、有限 FD 集 F 与覆盖 U 的分解 D = { U 1 , … , U m } 。值论域固定为无限集合,实例是无 NULL 的有限元组集合。先假定 m ≥ 1 ;空属性集也可按同一规则处理。
建立 m 行、| U | 列的符号表 T 。每列 A 有一个共同的标记符号 a A ;第 i 行在 U i 内的列填 a A ,其余列填该格私有的新符号 b i A 。不同列的符号组不相交,所有私有符号起初互异。a A 表示待检验的拼接元组在 A 列的值,b i A 表示相应见证行尚未被指定的值。两者都是待赋值符号,不是 SQL NULL,也不是预先规定不能合并的数据库常量。
对一条 L → M ∈ F ,若两行在 L 的每列上具有相同符号,就要求它们在 M 的每列也相同。对于仍不同的一对右侧符号,在该列全表 合并其所有出现。若其中一个是 a A ,始终以 a A 为代表;否则按固定编号选一个代表。这称为一次有效合并。不能只修改触发规则的两个单元格而保留别处旧符号,否则同一个符号会被当成两个不同的值。
FD 追赶判据。 反复应用规则直到不存在有效合并。终止表有一行恰为
a = ( a A : A ∈ U ) 当且仅当 D 对 F 无损。这个判据使用原来的全部 F ;它与投影依赖能否局部保持 F 是不同的检查。
一个明确终止的实现
下面每次只做一次有效符号合并,然后从头扫描。这样不需要猜测一轮中哪些后续规则被新等式启用,也不会漏掉先前扫过的行对。
text T := 按分解初始化的符号表
repeat
在所有 FD L → M、所有不同行对 i,j 中寻找:
T[i,L] = T[j,L],但某个 A ∈ M 满足 T[i,A] ≠ T[j,A]
if 没有这样的三元组 (FD, 行对, A):
return T
按 a_A 优先、否则固定编号的规则选代表
在 A 列全局替换另一符号为代表
1 2 3 4 5 6 7 8
这里所有 FD 与行对都在有限搜索范围内;只有搜索完仍找不到有效合并,才是固定点。若中途已出现全标记行,可以提前回答“无损”,因为后续合并不会破坏它;若要输出终止表,就继续执行上述循环。没有看到成功行时必须真正到固定点,不能任意提前回答“有损”。
直觉
假设有人声称某元组 u 在全部投影的连接中。每个组件都应有一条原表见证行,在这个组件的列上与 u 相同。符号表把这些见证先排列出来:标记格记录已经与 u 对齐的列,私有格保留各见证的其余部分。
FD 逐渐迫使见证之间共享更多值。例如两条见证已经共享 A ,A → B 就使它们也共享 B 。若最终有一条见证在所有列上都与 u 对齐,就说明 u 本来是原表中的那一行,拼接没有制造新元组。若怎样合并都到不了这一状态,终止表本身可以变成一张合法数据表,让缺少的全标记行成为具体伪元组。
例子与边界
两次合并产生成功行
继续使用
U = A B C D , F = { A → B , B C → A , C → D } . 对 D B = { A B , A C , C D } ,初始表为:
组件
A
B
C
D
A B
a A
a B
b 1 C
b 1 D
A C
a A
b 2 B
a C
b 2 D
C D
b 3 A
b 3 B
a C
a D
第一、二行的 A 相同,应用 A → B ,令 b 2 B 合并到 a B 。第二行成为 ( a A , a B , a C , b 2 D ) 。第二、三行的 C 相同,应用 C → D ,再把 b 2 D 合并到 a D 。得到:
组件
A
B
C
D
A B
a A
a B
b 1 C
b 1 D
A C
a A
a B
a C
a D
C D
b 3 A
b 3 B
a C
a D
A C 行已经全标记,所以分解无损。继续检查时,共享 A 的前两行已经共享 B ;共享 C 的后两行已经共享 D ;没有两条不同的行共享 B C ,因此 B C → A 不产生额外合并。这张表确实也是固定点。
图片加载失败 图只追踪 A C 这一条见证如何补齐两格;参与触发的 A B 、C D 行仍属于完整符号表。前页已经证明这套 BCNF 设计不保持 B C → A ,而这里仍成功证明无损,两者相容。追赶不是用局部检查替代原依赖,而是在假定原表满足全部 F 的前提下传播等式。
有损设计的固定点不能省略
把中间组件换成 B C ,得到 D L = { A B , B C , C D } :
组件
A
B
C
D
A B
a A
a B
b 1 C
b 1 D
B C
b 2 A
a B
a C
b 2 D
C D
b 3 A
b 3 B
a C
a D
第二、三行共享 C ,故 C → D 把 b 2 D 合并为 a D 。终止表为:
组件
A
B
C
D
A B
a A
a B
b 1 C
b 1 D
B C
b 2 A
a B
a C
a D
C D
b 3 A
b 3 B
a C
a D
现在三行 A 各不相同,A → B 无法触发;第一、二行虽然共享 B ,却不共享 C ,第二、三行虽然共享 C ,却不共享 B ,因而 B C → A 也无法触发;共享 C 的两行已经在 D 上相同。全部规则都检查过,确实没有有效合并,而没有任何一行全标记。
把表中不同符号直接解释成互异普通值,三行便组成满足 F 的关系。全标记元组在 A B 投影中由第一行见证,在 B C 投影中由第二行见证,在 C D 投影中由第三行见证,所以它属于连接;它却不在原三行中。这样得到的是满足全部原约束的有损反例。前页另给了一张更小的两行原表并列出四行连接结果,便于手算;这里展示的是算法每次失败都能统一产生的证书。
本页为什么不需要生成新行
FD 只要求已有行之间的一些值相同,所以本算法只做等式合并。若约束要求“每条 P ( x ) 都应有某条 Q ( x , y ) ”,就可能需要增加事实与新的见证值;当前终止计数不再适用。合取查询 公理库 合取查询 Conjunctive query · CQ · Select-project-join query 用关系事实的匹配定义集合答案,并以规范数据库与反向同态判定所有有限实例上的查询包含。 页讨论过这种带约束语义与无约束规范数据库判据的差别。不能把本页的终止性推广为所有 chase 都终止,也不能直接用本表格判据决定任意约束下的查询包含。
推论与应用
终止、不变量与成本
初始化至多有 m n 个实际出现的符号,其中 n = | U | 。每次有效合并使符号等价类数严格减少,不增加任何行、列或符号,所以合并次数少于 m n (零属性时直接终止)。标记符号永远优先保留,因此组件行原来具有的标记格永远保持标记,已经全标记的行也不会退化。这两个不变量分别支持失败反例中的投影见证与成功时的提前停止。
令 ℓ = 1 + ∑ L → M ∈ F ( | L | + | M | ) 。用直接存储符号编号的数组实现,一轮寻找有效合并需要 O ( m 2 ℓ ) 次基本操作;保守地扫描整表完成一次全局替换,耗时 O ( m n ) 。故总时间有简单多项式上界
O ( ( m n + 1 ) m 2 ℓ + ( m n ) 2 + m n ) , 存储为 O ( m n + ℓ ) 个属性或符号编号。这里按编号比较、替换的单位成本计数;若计比特,还需相应的编号位长。更好的索引或并查集实现可以减少重复扫描,本页的正确性不依赖这些优化。
全标记行为什么证明所有合法实例无损
取任意 r ⊨ F ,以及任意 u ∈ ⋈ i π U i ( r ) 。对每个组件 U i 选一个见证 t i ∈ r ,满足 t i [ U i ] = u [ U i ] 。给初始表赋值:a A 取 u [ A ] ,私有 b i A 取 t i [ A ] 。同一个标记符号在不同组件出现时,其值都是同一个 u [ A ] ,所以这是一份一致的赋值,而且第 i 行恰解释为原表的见证 t i 。
归纳考虑一次规则触发。触发的两行在左侧使用相同符号,因而其见证在左侧取值相同。原关系满足这条 FD,所以右侧待合并符号的赋值也相同。合并并全局替换后,赋值仍一致,每行仍解释为原来的 t i 。这是追赶的见证赋值不变量。
若某行最终全标记,该行的见证 t i 在每列都取 u [ A ] ,因此 t i = u ,从而 u ∈ r 。于是连接结果包含于原表;与始终成立的反向包含结合,得到无损。证明使用任意 r , u ,所以并非只检查了一张示例数据库。
没有全标记行为什么一定有合法反例
设已经达到固定点且没有全标记行。利用无限论域,为最终不同的符号选择互异普通值,解释各行得到有限关系 r T ;重复的行按集合语义合并即可。若 r T 中两行在某条 FD 的左侧相同,由于不同符号取不同值,它们在符号表左侧也逐格相同。固定点意味着右侧已逐格相同,否则还可有效合并。因此 r T ⊨ F 。
每个组件对应的那一行始终保留该组件中的全部标记格,所以全标记元组 a 的 U i 投影属于 π U i ( r T ) 。这对所有组件成立,故 a ∈ ⋈ i π U i ( r T ) 。但终止表没有全标记行,且不同符号解释为不同值,所以 a ∉ r T 。这就是合法有限反例,证明分解有损,也完成判据的另一个方向。
这种证明与合取查询的规范数据库思路相通:把符号对象冻结成具体有限实例,以一次见证或一次失败承载全实例命题。区别是原始表未必满足 FD,必须先追赶到满足约束的固定点,再把它当作反例。规则应用顺序可以改变中间表和私有代表名称,但上述两个方向保证:任何真正到固定点的执行都给出同一个无损判定。
参考资料