原著链接:[Database.System.Concepts(6th.Edition.2010)].Abraham.Silberschatz.文字版[www.xuexi111.com].pdf
原著作者:Silberschatz, Abraham/ Korth, Henry F./ Sudarshan, S.
使用函数依赖进行分解
在8.1章,我们提出了方法论关于判定一个关系模型的图解是否需要被拆分,这个方法是基于码和函数依赖的概念。
在讨论设计关系数据库的模型算法时,我们需要讨论的是行列表中任意关系以及他们的模型图解,而不是单一的例子。回忆一下我们第二章对于关系模型的介绍,我们现在总结一下知识点。
1.大致来说,我们用不同的希腊字母代表不同种类元组的特性(比如,α)。我们用小写罗马字母加上在圆括号内的大写罗马字母指代模型图解(比如,r(R))。我们用标记r(R)代表含有R的关系r,指示元组每一类的特性,但有时当关系模型名称并不重要时我们只用R简化我们的标记。
当然,一个关系模型图解是一类元组的特性,而不是所有的特性的集合都是模型图解。当我们用小写的希腊字母,那些涉及到分类的特性,也许会,也许不会成为我们需要的模型图解。当我们想要代表类的特性时,一个罗马字母就可以明确地表示一个模型图解。
2. 当一类元组的特性被称为超键时,我们用K代表。超键是个特定的关系模型图解,所以我们有术语“K是r(R)的超键”。
3. 我们用小写字母命名模型之间的关系,在我们的例子中,这些命名可以放入现实生活中(比如,指导教师)。因此,在我们的定义和算法中,我们用小写字母代表关系,就像r。
4. 当然,一种关系,在任何被需要的时间里都是有自身的独特价值的,我们指的是,以关系作为一个例子并且用术语“r关系”表示。当我们都很清楚对方在讨论这个例子时,我们就可以用简单的关系名(比如关系r)。
8.31 键与功能从属性
一个数据库在现实生活中模拟一类的实体和关系。在现实生活中,对于数据通常有繁多的约束(规则)。就比如说,在一所大学的数据库中,那些被倾耳戴目地保留下来的规矩有:
1. 学生和指导教师的鉴别只能依靠各自的ID
2. 每个学生和指导教师只拥有唯一的姓名
3. 每个指导教师和学生(主要)通过唯一的系所联系
4. 每个系的预算只能有一种取值,并且只能用来维持学科建设。
关系中的例子满足现实生活中的所有约束则被称为关系中的合法举例。一个所有关系的举例都是合法的数据库中任意的合法的举例都是唯一的(???这句话我真的不确定要怎么翻译)
一些现实生活中最普通的被广泛使用的不同种类的约束可以被正式地代表,就像键(超键,候选键和主键)又或者像我们接下来要定义的功能的从属性。
在2.3章,我们从总体上定义了超键的概念,作为一组元组中一个或多个的特性。这促使我们去定义一个关系在元组的集合。在这里我们重申以下概念的定义:r(R)是关系模型的图解,R的一个自己K是r(R)的超键,在任何合法的r(R)的举例里,r中的元组每一对t1,t2。若t1≠t2,则t1[K]≠t2[K]。因此,在关系r(R)中的任意合理的举例不可能存在两个相同的元组在特质分类K中拥有相同的价值。毋庸置疑的是,在r中如果没有两个元组在K里拥有相同的取值,并且在r中元组中的K值是唯一确定的。
鉴于一个超键代表一类元组的特性,这个特性可以唯一确定一个完整的元组,功能的从属性则允许我们去表达那些已知的特性的取值的约束,同理,这些约束也是唯一确定的。试想一下r(R)作为一个关系模型图解,并且α⊆R和β⊆R。
以r(R)举例,我们认为这个例子代表了功能的从属性α→β,若元组中所有不相等的t1,t2满足t1[α]=t2[α] 则在这种情况下t1[β]=t2[β]。
我们通常说若功能的从属性α→β代表了图解r(R),则在r(R)的每一个合理的举例里都满足了功能的从属性。
使用功能从属性的标记时,我们默认K是r(R)的超键若功能从属性K→R代表r(R).换言之,在每个合法的r(R)举例中,在每一对举例中的元组t1,t2中,K是该元组的超键,无论何时t1[K]=t2[K]。同理可知,t1[R]=t2[R](前提是t1=t2)
功能从属性鼓励我们表达那些不能用超键表达的约束,在8.12章,我们拿这个模型图解举例:
固有成分(ID编号,姓名,薪资,科室名,学科建设,预算)
在这个例子里科室名→预算代表着所谓的功能从属性,因为在每个部门(可被科室名唯一确定)都只拥有唯一的预算取值。
这反映了一个事实:每一组的元组特性(ID编号和科室名)都可以通过表示:ID编号,科室名称→姓名,薪资,学科建设,预算 给学院-科室形成超键。
我们通过两种方式使用功能的从属性。
1. 通过测试关系模型里的例子观察他们是否满足功能从属性里提供的F组。
2. 通过指定合理关系内的组类的约束,我们应该重视当我们只拥有能满足一类的功能从属性的关系实例。如果我们希望只考虑模式R上满足函数依赖集F的关系,我们就说F在r(R)上成立。
让我们考虑图8-4中的关系r的实例,看看它满足什么函数依赖。注意到A→C是满足的。存在两条元组的A值为a1。这些元组具有相同的C值,c1。类似地,A值为a2的两条元组具有相同的C值,c2。没有其他元组对具有相同的A值。但是,函数依赖C→A是不满足的。为了说明这一点,考虑元组t1=(a2,b3,c2,d3)和元组t2=(a3,b3,c2,d4)。这两条元组具有相同的C值,但他们具有不同的A值,分别为a2和a3.因此,我们找到一对元组t1和t2,使得t1[C]=t2[C],但t1[A]≠t2[A]。
有些函数依赖称为平凡的,因为它们在所有关系中都满足。例如,A→A在所有包含属性A的关系中满足。从字面上看函数依赖的定义,我们知道,对所有满足t1[A]=t2[A]的元组t1和t2,有t1[A]=t2[A]。同样,AB→A在所有包含属性A的关系中满足。一般地,如果β⊆α,则形如α→β的函数依赖是平凡的。
重要的是,要认识到一个关系实例可能满足某些函数依赖,它们并不需要在关系的模式上成立。在图8-5的classroom关系的实例中,我们发现room_number→capacity是满足的。但是,我们相信,在现实世界中,不同的建筑里的两个教室可能具有相同的房间号,但具有不同的空间大小。因此,有时有可能存在一个classroom关系的实例,不满足room—number→capacity。所以,我们不将room_number→capacity包含于classroom关系的模式上成立的函数依赖集中。然而,我们会期望函数依赖building,room_number在classroom模式上成立。
给定关系r(R)上成立的函数依赖集F,有可能会推断出某些其他的函数依赖也一定在该关系上成立。例如给定模式r(A,B,C),如果函数依赖A→B和B→C在r上成立,可以推出函数依赖A→C也一定在r上成立。这是因为,给定A的任意值,仅存在B的一个对应值,且对于B的那个值,只能存在C的一个对于值。我们稍后在8.4.1节学习如何进行这种推导。
我们将使用F+符号来表示集合的闭包,也就是能够从给定F集合推导出的所有函数依赖的集合。显然,F+包含F中所有的函数依赖。
8.3.2 Boyce-Codd范式
我们能达到的较满意的范式之一是Boyce-Codd范式(Boyce-Codd Normal Form,BCNF)。他消除所有基于函数依赖能够发现的冗余,虽然,如我们在8.6节中看到的,可能有其他类型的冗余还保留着。具有函数依赖集F的关系模式R属于BCNF的条件是,对F+中所有形如α→β的函数依赖(其中α⊆R和β⊆R),下面至少有一项成立:
l α→β是平凡的函数依赖(即,β⊆α)。
l α是模式R的一个超码。
一个数据库设计属于BCNF的条件是,构成该设计的关系模式集中的每个模式都属于BCNF。
我们已经在8.1节中见过了不属于BCNF的关系模式的例子:
inst_dept(ID,name,salary,dept_ name,building,budget)
函数依赖dept_ name→budget在inst_dept分解为instructor和department是一个更好的设计。模式·instructor属于BCNF。所有成立的非平凡的函数依赖。例如:
ID→name,dept_name,salary
在箭头的左侧包含ID,且ID是instructor的一个超码(事实上,在这个例子中,是主码)(也就是说,不包含ID的另一侧上,对于name,dept_name和salary的任意组合不存在非平凡的函数依赖。)因此,instructor属于BCNF。
类似的,department模式属于BCNF。因为所有成立的非平凡函数依赖,例如:
Dept_name→building,budget
在箭头的左侧包含dept_name,且dept_name是department的一个超码(和主码)。
因此,department属于BCNF。我们现在讲述分解不属于BCNF的模式的一般规则。设R为不属于BCNF的一个模式。则存在至少一个非平凡的函数依赖α→β,其中α不是R的超码。我们在设计中用一下两个模式取代R:
l (α U β)
l (R-(β-α))
在上面的inst_dept例子中,α=dept_name,β={building,budget},且inst_dept被取代为:
l (α U β)=(dept_name,building,budget)
l (R-(β-α))=(ID,name,dept_name,salary)
在这个例子中,结果β-α=β。我们需要向上述那样的表述规则,从而正确处理箭头两边都出现的属性的函数依赖,技术上的原因我们会在8.5.1节介绍。
当我们分解不属于BCNF的模式时,产生的模式可能有一个或多个不属于BCNF。在这种情况下,需要进一步分解,其最终结果是一个BCNF模式集合。
8.3.3 BCNF和保持依赖
我们已经看到多张表达数据库一致性约束的方式:主码约束,函数依赖,Check约束,断言和触发器。在每次数据库更新时检查这些约束的开销很大,因此,将数据库设计成能够高效地检查约束是很有用的,特别地,如果函数依赖的检验仅需要考虑一个关系就可以完成,那么检查这种约束的开销就很低。我们将看到,在有些情况下,到BCNF的分解会妨碍对某些函数依赖的高效检查。
对此举例,假定我们对我们的大学机构做一个小的改动。在图7-15的设计中,一位学生只能有一位导师,这是由从student到advisor的联系集advisor为多对一而推断出的。我们要做到“小”改动是一个教师只能和单个系关联,且一个学生可以有多个导师,但是一个给定的系中至多一位。
利用E-R设计实现这个改动的一种方法是,把联系集advisor替换为涉及实体集instructor,student和department的三元联系集dept_advisor,它是从{instructor,student}对到department多对一的,如图8-6所示。该E-R图指明了“一个学生可以有多位导师,但是对应于一个给定的系最多只有一个”的约束。
对于新的E-R图,instructor,department和student的模式没有变。然而,从dept_name导出的模式现在为:
dept_advisor(s_ID,i_ID,dept_name)
虽然没有在E-R图中指明,不过假定我们有附加的约束“一位教师只能在一个系担任导师。”那么,下面的函数依赖在dept_advisor上成立:
i_ID→dept_name
s_ID,dept_name→i_ID
第一个函数依赖产生于我们的需求:“一位教师只能在一个系担任导师”。第二个函数依赖产生于我们的需求:“对于一个给定的系,一个学生可以有至多一位导师”。
注意,在这种设计中,每次一名教师参与一个dept_advisor联系的时候,我们都不得不重复一次系的名称。我们看到dept_advisor不属于BCNF,因为i_ID不是超码。根据BCNF分解规则,得到:
(s_ID,i_ID)
(i_ID,dept_name)
上面的两个模式都属于BCNF。(事实上,你可以验证,按照定义任何只包含两个属性的模式都属于BCNF。)然而注意,在BCNF设计中,没有一个模式包含函数依赖s_ID,dept_name→i_ID中出现的所有属性。
由于我们设计使得该函数依赖的强制实施在计算上很困难,因此我们称我们的设计不是保持依赖的(dependency preserving)。由于常常希望保持依赖,因此我们考虑另外一种比BCNF弱的范式,它允许我们保持依赖。该范式称为第三范式。
8.3.4
BCNF 要求所有非平凡依赖都是形式的一B, 其中 a 是 主键。第三范式 (3NF) 通过允许某些不平凡的功能依赖, 它的左端不是 主键, 稍微放松这个约束。记得我们之前定义的3NF, 候选键是一个最小的 主键-即, 主键 没有适当的子集, 这也是一个 主键。
关系架构 R 与函数依赖项的集合 f 的第三个法线形式有关, 如果对于窗体α > β的 f + 中的所有功能依赖性, 其中α属于 r 和β属于 r, 至少有以下任一项:
l α属于β 是一个微不足道的功能依赖性.
l α是 R 的 主键。
l β一α中的每个属性 a 都包含在 R 的候选码中。
注意上面的第三个条件并不表示单个候选码必须包含 B一a 中的所有属性。B一a 中的每个属性 A 都可以包含在不同的候选键中。
前两种选择与 BCNF 定义中的两种替代方案相同。3NF 定义的第三个备选方案似乎相当不直观, 而且不清楚为什么它是有用的。在某种意义上, 它代表了 B 的最小松弛 (NF 条件, 这有助于确保每个架构都具有依赖项保留分解为3NF。当我们研究分解为3NF 时, 它的目的将变得更加清晰。
注意任何满足 BCNF 的模式也满足 3 nf, 因为它的每个功能依赖项都将满足前两种选择之一。BCNF 因此是一个限制性更强的正常形式比3NF。
3NF 的定义允许某些功能依赖项不
允许在 BCNF。在 BCNF 中不允许仅满足3NF 定义的第三种选择的依赖项, 但允许在3NF 中一条。
现在, 让我们再次考虑 dept_advisor 关系集, 它具有以下功能依赖性:
i_ID 一dept_name
i_ID 一dept_name一i_name
从技术上讲, 其属性不全部显示在任何一个架构中的依赖关系仍然是隐式强制执行的, 因为存在其他依赖项, 这意味着逻辑上我们在后面的8.4.5 节中处理该情况。你可能已经注意到, 我们跳过第二个正常的表格, 只在历史意义上, 并没有在实践中使用。这些依赖项是可传递依赖项的示例 (参见练习练习 8.16)。3NF 的原始定义是在传递依赖性方面。我们使用的定义是等价的, 但更容易理解。
在8.3.3 节中, 我们争辩说, 功能依赖性 "i_ID 一dept_name" 导致 dept_advisor 架构不在 BCNF 中。注意这里α = i_ID, b = dept_name, 和 b a = dept_name。由于功能依赖项 s_ID, dept_name 一i_ID 在 dept_advisor 上保留, 因此属性部门名称包含在候选键中, 因此 dept_advisor 位于3NF。
我们已经看到的权衡, 必须在 BCNF 和3NF 之间, 当没有依赖关系保存 BCNF 设计。8.5.4 节更详细地介绍了这些权衡。
8.3.5 更高的范式
使用功能依赖项分解架构可能不足以避免某些情况下不必要地重复信息。考虑一下讲师实体集定义中的细微变化, 我们会在其中记录一组儿童姓名和一组电话号码。电话号码可以由多个人共享。因此, 电话号码 和 child_name 将是多值属性, 按照我们从电子 R 设计中生成架构的规则, 我们将有两个架构, 一个用于每一个多值属性、电话号码 和 child_name:
(ID,child_name)
(ID,phone_name)
如果我们要结合这些模式来获得
(ID,child_name,phone_name)
我们会发现结果是在 BCNF, 因为只有非平凡的功能依赖性持有。因此, 我们可能会认为这样的组合是一个好主意。然而, 这样的组合是一个坏主意, 我们可以看到, 通过考虑一个教师与两个孩子和两个电话号码的例子。
例如, 让 ID 为99999的讲师有两个名为 "David" 和 "威廉" 的孩子, 两个电话 numbers,5125551234 和 5125554321.在组合架构中, 我们必须为每个从属关系重复一次电话号码:
如果我们没有重复电话号码, 只存储第一个和最后一个元组, 我们将记录从属名称和电话号码, 但是得到的元组将意味着大卫相当于51 2-555-1234,而威廉相当于我们知道512-555-4321.就如我们所知道的一样,这是不正确的。
因为基于函数依赖正常形态不足以应付这样的情况下,其他的依赖关系和正常的形式已经确定。我们在第8.6和8.7覆盖这些。
8.4 函数依赖理论
我们已经看到在我们的例子中,它是能够系统地推理函数依赖作为测试模式的BCNF或3NF的进程的一部分是有用的。
8.4.1 函数依赖集的闭包
我们将看到,给定一个模式函数依赖的集合F,我们可以证明某些其他功能依赖也保持在schema.We说,这样的功能依赖关系“逻辑隐含”由F.当正常形式的测试,这是不够的考虑给定函数依赖的; 相反,我们需要考虑的是在架构容纳所有函数依赖。
更正式地,给定一个关系模式R(R),R上的函数依赖f为逻辑上由于r的一组函数依赖˚F暗示如果r的每个实例(R)满足˚F也满足F。
假设我们给出的关系模式R(A,B,C,G,H,I)和所述一组函数依赖的:
A→B
A→C
CG→H
CG→H
B→H
那么函数依赖
在逻辑上是隐含的。即, 我们可以表明, 每当一个关系满足我们给定的功能依赖集时, 一个 H 也必须满足该关系。假设元组 t1 和t2满足:
t1[A]=t2[A]
由已知A→B,因此u由函数依赖定义推出
t1[B]=t2[B]
那么,又由于已知B→H,因此由函数依赖的定义推出
t1[H]=t2[H]
因此,我们证明了,对任意两个元组t1及t2,只要t1[A]=t2[A],就一定有t1[H]=t2[H],而这正是A→H的定义。