阅读本文你能学到
- 如何理解最小覆盖
- 如何求解最小覆盖
我选择比较通俗的语言简单说说最小覆盖是什么,而不是直接套用教科书上的定义;然后再用一个例子来讲解如何求解。
1. 如何理解
【注】 本部分纯个人理解,如有错误敬请指正。
数据库设计的目的是什么?
- 其中一个目的当然是为了 准确无误的保存信息。
假想一张表 peopel(name, sex),每个人都有性别:男或女(除了西方抽象的一百多种-_-)。
倘若它里面有记录 (‘张三’, ‘男’),(‘张三’, ‘女’),你觉得这样的信息是正确的吗?但如果加入约束(函数依赖):name -> sex,即 name 相同时,sex 也一定相同。是不是就不会出现上述记录了;
也就是说 sex 因为 name 的约束,它的存在才有价值,它才能被唯一标识。
如果没有 name,单谈论 sex,这样的表是没有意义的,因为你不知道 “张三”、“李四” 的性别是什么?
但加入了 name -> sex,告诉你 “张三, 女”,并且一定不会存在 “张三,男”。
那么 sex 因为 name 而被唯一标识,是不是这样的信息对我们来说更有意义。
换句话说,一个函数依赖集包含了一个关系模式的约束信息。
- 另外一个要求是 没有冗余
我认为这也是提出最小覆盖的原因。看一个例子:
有一张表 people(id, name, sex),满足 (id, name) -> sex;加个约束:“id 与 name 都不可能重复”。
假设 id 与 name 等价,这里不讨论他们的关系
那么 (0, ‘张三’,‘女’) 这样的信息对于我们来说有冗余的,因为你只需要知道 (0, ‘女’) 或 (‘张三’, ‘女’),就能将 sex 唯一标识出来,也就是说:
(id, name) -> sex 是个部分函数依赖,存在冗余;将它替换为 id -> sex、name -> sex 就可以了。并且,上述的两个函数依赖只需要保留一个,同时保留也会造成冗余。
换句话说,函数依赖 {(id, name) -> sex} 或者 {id -> sex, name -> sex} 不是 “最小” 的,它可以被 {id -> sex} (或者 {name -> sex})替换,同时 没有任何信息丢失。
下面来看定义
- (1) 没什么好说的,就告诉你必须这么做,比如
A -> BC
应使用分解律换为
A -> B
A -> C
- (2) 等价于 去冗余:
就好比之前的 id -> sex、name -> sex 只能保留一个,否则有冗余信息。
第二个 people 的函数依赖集
F = {id -> sex, name -> sex},
那么 {id -> sex} 与 F 是等价的,
"前提是 id 与 name 是等价的"
因此 F 中的函数依赖只需保留一个;
- (3) 为了 消除部分函数依赖:
比如第一个 people(name, sex),考虑
1. (name, sex) -> sex
显然这是一个部分函数依赖,因为
2. name -> sex
因此 1 应该被 2 替换
通过此你能看出它的解法有三步:
- 右部最小化
- 左部最小化(消除部分函数依赖)
- 整体最小化(去冗余)
同时,你也会发现:在此过程中,函数依赖集 F 被最小化为 M,并且没有任何的信息丢失,也就是说 F 与 M 等价
2. 如何求解
【例】
求 F = {
C -> A,
A -> G,
CG -> B,
B -> A
}
的最小覆盖。
2.1 右部最小化
利用分解律,不必多说:看到函数依赖的右部是多个字母的,给它换为一个;例题中没有。
2.2 左部最小化
显然看是否需要改动
CG -> B
那么只需要依次计算 C、G 的闭包
1. C+ = {A, B, C, G}
2. G+ = {G}
由于 C+ 包含了 B (CG->B 的 B),说明 CG -> B 是部分函数依赖(换句话说,‘CG’ 有没有 ‘G’ 都无所谓,因为 ‘C’ 就能将 B 唯一标识出),因此去掉 G 换为
C -> B
现在新的函数依赖集
F = {
C -> A,
A -> G,
C -> B,
B -> A
}
2.3 整体最小化
核心思想为:删除某条函数依赖后,新的函数依赖集是否有信息丢失?
来看第一个
C -> A
将它先从 F 中删除,此时
F = {
A -> G,
C -> B,
B -> A
}
那么新的 C+ = {A, B, G},
因为删除的是 C -> A,故计算新的 C+
其中 A (C -> A 的 A) 属于 C+。也就是说明:
当 C -> A 被去掉后,在新的 F 下,C 仍然能将 A (C -> A 的 A) 唯一标识出来;换句话说, C -> A 即使被删除了也没有信息丢失,因此 C -> A 是冗余的,需要被移出。
其实如果你画一个 F 的依赖图,你会发现它存在传递函数依赖,被删除的函数依赖可以通过其他的推导出来,也就是说它是冗余的
那么最后的结果为
F = {
A -> G,
C -> B,
B -> A
}
你可以自行继续删除其他的,可以发现删除后信息都会丢失,因此这是最终结果。
这里再看一个问题,也是本人曾经遇到的问题:错误地认为
只需要删除右部相同的的函数依赖集的其中一个即可,来看上面的例子:
此时
F = {
C -> A,
A -> G,
C -> B,
B -> A
}
之前说了需要删除
C -> A
那么删除另外一个右部跟它相同可以吗?也就是删除
B -> A
行不行呢?
当删除它后,
F = {
C -> A,
A -> G,
C -> B
}
此时 B+ = {B},而 A (B -> A 的 A) 不属于 B+,也就是说,当 B -> A 被删除后,A 就不能通过 B 被唯一标识出(即便有 C -> A,但是 B -> A 这部分信息就丢失了),因此有信息丢失,所以不能删除。
你也可以画一画它的依赖图,看看为什么删除后会有信息丢失,相信你会有更深的理解
最后
本文一部分来源于教科书、教学视频,一部分来源于个人理解,如有错误,敬请指出。