来源:《Database System Concepts》,
作者:
亚伯拉罕Silberschatz耶鲁大学
亨利·科尔斯·列高大学
孟买,S.苏达山印度理工学院
8.3分解使用函数依赖
在8.1章节中,我们注意到,有一种正式的方法评估关系模式是否应该被分解。这种方法基于键和数据依赖的概念。
在讨论关系数据库设计的算法时,我们需要讨论任意关系和它们的模式,而不是只讨论例子。回顾我们在第二章中介绍的关系模型,我们在这里总结了我们的记法。
通常,我们使用希腊字母来表示一组属性(例如a)。我们使用一个小写罗马字母,后面是大写罗马字母作为插入部分,以表示关系模式(例如,r(R))。我们使用符号r(R)来表示关系r的模式,R表示一组属性,但有时当关系名对我们不重要的时候我们将我们的符号简化为R。
当然,一个关系模式有一系列的属性,但是并不是所有的属性都是模式。当我们使用小写希腊字母时,我们指的是一组属性,这些属性可能是或不是一个模式。当我们希望指出属性集绝对是一个模式时,就使用罗马字母。
当一组属性是主码时,我们用K表示它。一个主码属于一个特定的关系模式,所以我们使用术语“K是r(R)的主码。”
我们用小写的名称表示关系。在我们的示例中,这些名称是为了现实的(例如,教师),而在我们的定义和算法中,我们使用的是单个字母,比如r。
当然,一个关系在任何给定的时间都有一个特定的值;我们指的是作为实例和使用术语“r的实例”。这是明确的,当我们讨论一个实例时,我们可以简单地使用关系名称(例如,r)。
8.3.1 键与函数依赖
数据库模拟现实世界中的一些实体与关系。在现实世界中,数据通常会有一些限制(规则)。例如:
在大学数据库中会有一些约束条件:
1.学生和教导员的身份通过各自的省份证号被识别。
2.每个学生和教导员有唯一的名字。
3.每位指导员和学生(主要)通过唯一的部门联系。
4.每个部门对于预算只有唯一的值,并且只与唯一建筑相联系。
一个满足所有这些现实约束的关系的实例称为关系的合法实例;数据库的合法实例是所有关系实例都是合法实例的集合。在现实世界中,最常用的一些类型的约束可以被正式地表示为键(超键、候选键和主键),或者作为函数依赖,我们在下面定义它:
而超键是一组唯一标识整个元组的属性,而函数依赖则允许我们表达约束,以唯一地标识某些属性的值。考虑一个关系模式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都是一个超级键,当t1[K] = t2[K]时,也就是t1[R] = t2[R](也就是t1 = t2)。
功能依赖允许我们表达我们无法用超键表达的约束。在第8.1.2节中,我们考虑了模式:
inst dept (ID, name, salary, dept name, building, budget)
在函数依赖 dept name → budget 关系是存在的因为每个部门能够被唯一的预算识别。
我们表示这两个属性(ID, dept名称)通过写入来形成inst _dept的超键:
ID, dept name→name, salary, building, budget
我们将以两种方式使用函数依赖:
1.测试关系的实例,看看它们是否满足给定的函数依赖F。
2.明确合法关系的约束。因此,我们只关心那些满足给定的函数依赖集的姿态之间的关系。如果我们想要约束自己与模式r(R)的关系,它满足函数依赖的集合F,我们说F成立。
让我们考虑图8.4的关系r的实例,看看哪些函数依赖项得到了满足。观察到C函数依赖于A。这里有两个元组
A有a1值,这些元组具有相同的C值,即c1。类似地,两个具有A值的元组具有相同的C值,c2。没有其他成对的不同元组具有相同的值。然而,函数依赖关系C . A并不满足。要知道它不是,考虑元组t1 =(a2,b3,c2,d3)和t2 =(a3,b3,c2,d4)。这两个元组有相同的C值,c2,但是它们有不同的值,分别是a2和a3。因此,我们发现了一对元组t1和t2,这样t1[C] = t2[C],但是t1[A] 不等于 t2[A]。
已知一组函数依赖项F在关系r(R)上成立,它可能也可以推断出其他函数依赖保存子关系中,例如,给一个
r(A, B,C)模型,如果B函数依赖A并且C函数依赖于B,我们就可以推断出C函数依赖于A。这是因为这是因为,考虑到A的任意值只能是B的一个对应的值,对于B的值,只能有一个对应的c值。我们稍后将在第8.4.1节中学习如何做出这样的推论。
我们将使用符号F+来表示集合F的闭包,也就是说,所有函数依赖项的集合都可以被推断出来,因为F+包含了F中所有的函数依赖项。
8.3.2 BCNF
我们能得到的最理想的范式之一是BCNF。它消除了基于功能依赖的所有冗余,但是,正如我们将在8.6部分中看到的,可能还有其他类型的冗余。一个关系模式R在BCNF中,关于函数依赖关系的集合F,如果,对于F+形式的所有函数依赖性α→β,α⊆R和α⊆R,至少有一个是这样的:
α→β是一个微不足道的函数依赖项(也就是β⊆α )。
α模式R的超码
如果在BCNF中组成了设计的关系模式集的每个成员,那么数据库设计就是在BCNF中。
我们已经在8.1部分中看到了一个关系模式的例子,它不在BCNF:
Inst_ dept (ID, name, salary, dept name, building, budget)
函数依赖Inst_ dept→budget继续Inst_ dept,但是Inst_ dept并不是一个超码(因为一个部门可能有很多不同的指导者)。在8.1.2部分中,我们看到inst_dept分解为nstructor 和department是一个更好的设计。讲师范式在BCNF中。所有包含的非平凡的函数依赖项,例如:ID→name, dept_ name, salary
在箭头的左边包含ID,ID是一个用于讲师超码(实际上,在本例中是主键)。(换句话说,不存在任何不重要的函数依赖和组合关于name, dept_name, andsalary,没有ID,就在一边。)因此,讲师在BCNF中。
类似地,department模式是在BCNF中,因为所有的重要的函数依赖项,例如:
dept name→building, budget
在箭头的左边包含dept _name,dept _name是一个用于部门的超码(和主键)。
因此,部门在BCNF中。
现在我们声明一个用于分解的一般规则不是在BCNF中。让R成为不在BCNF中的模式。然后至少有一个非平凡的函数依赖项α→β,α不是r的超码。我们用两种模式代替了R。
(α∪β)
(R-(β-α)
以dept name为例, α=dept name, β ={building, budget}, 和inst _dept被取代
(α∪β)=(dept_ name, building,budget)
(R-(β-α)= (ID, name, dept_name, salary)
在这个例子中, 证明了β-α=β。我们需要像我们那样声明规则,以便正确地处理具有出现在箭头两端的属性依赖关系。这方面的技术原因稍后将在8.5.1部分中介绍。
当我们分解不在BCNF中的范式时,可能会有一个或多个产生的范式不在BCNF中。
在这种情况下,需要进一步分解,最终的结果是一组BCNF范式。
8.3.3 BCNF和依赖保留
我们已经看到了几种表达数据库一致性约束的方法:主键约束、功能依赖性、检查约束、断言和触发器。每次更新数据库时,测试这些约束都是非常昂贵的,因此,以一种能够有效地测试约束的方式来设计数据库是非常有用的。特别是,如果只考虑一个关系就可以测试函数依赖关系,那么测试这个约束的成本很低。我们将会看到,在某些情况下,分解为BCNF可以阻止对某些函数依赖的有效测试。
为了说明这一点,假设我们对我们的大学组织做了一个小小的改变。在图7.15的设计中,一个学生可能只有一个顾问。这是关系集顾问从学生到导师之间的关系。我们要做的“小”改变是,教师只能与一个部门联系,而学生可能有不止一个顾问,但最多只能由一个部门负责。
使用E-R设计实现此更改的一种方法是用三元关系集替换导师关系集,即dept_advisor,涉及到从{student,instructor}到部门的实体集instructor, student, 和department,如图8.6所示。E-R图表指定了“一个学生可能有不止一个顾问,但最多一个对应于一个指定部门”的约束。
有了这个新的E-R图表,教师、部门和学生的模式没有改变。然而,来自dept _advisor的模式现在是:
Dept_advisor (s_ID, i_ID, dept _name)
尽管在E-R图中没有指定,假设我们有一个额外的约束,“一个指导员可以作为一个单独的部门的顾问。然后,下面的函数依赖于dept_advisor:
I_ID→dept_name
s_ID, dept_ name→i_ID
第一个函数依赖来自于我们的要求:“一个指导员可以作为一个部门的顾问。第二个函数依赖来自于我们的要求,即“一个学生最多可以有一个给定部门的顾问。”
请注意,在这个设计中,我们必须在每次教师参与一个dept_advis或关系时,重复该部门的名称一次。我们看到,dept _advisor不在BCNF中,因为i_ID不是超码。按照我们对BCNF分解的规则,我们得到:
(s_ID, i_ID)
(i_ID, dept_name)
上述两种模式都是BCNF。(实际上,您可以通过定义来验证任何只有两个属性的模式都在BCNF中。)但是,请注意,在我们的BCNF设计中,没有模式包含所有出现在函数依赖项s_ID、dept_ name→i_ID中的属性。
因为我们的设计使它难以实现这种功能依赖,我们说我们的设计不是依赖于保护。由于依赖性保护通常被认为是可取的,我们考虑另一种比BCNF更弱的正常形式,这将允许我们保留依赖性。这个正常的形式叫做第三范式。
8.3.4第三范式
BCNF要求所有的非平凡的依赖关系都是表单的α→β,α是超码。第三个标准形式(3 NF)通过允许某些非平凡的功能依赖,而使其左边不是一个超码,从而稍微放松了这个约束。在我们定义3 NF之前,我们记得一个候选键是一个极小的超码——也就是说,一个超码没有属性子集也是一个超码。
一个关系chema R是第三个正常形式的函数依赖关系,如果,对于F+形式的α→β所有函数依赖性,α∈R和β∈R,至少有一个是这样的:
α→β是一个微不足道的函数依赖项。
α模式R的超码
每一个在β→α中的属性A都包含在R的候选键中。
注意,上面的第三个条件并不是说一个候选键必须包含所有β→α的属性;每一个β→α的属性A都包含在一个不同的候选键中。
前两个替代方法在BCNF的定义中与两个替代方法相同。第三个NF定义的第三个选择似乎不太直观,而且它为什么有用也不明显。从某种意义上说,它代表了BCNF条件的最小松弛条件,它有助于确保每个模式都有一个依赖关系,将其分解为3 NF。当我们研究分解为3 NF时,它的目的会变得更清楚。
请注意,任何满足BCNF的模式也满足3 NF,因为它的每个函数依赖项都满足前两个选项之一。因此,BCNF是比3 NF更严格的形式。
3 NF的定义允许在BCNF中不允许某些α→β函数依赖。在BCNF中不允许只满足3 NF定义的第三种选择的依赖关系,但在3 NF中是允许的。
现在,让我们再来考虑一下dept _advisor关系集,它具有以下的函数依赖:
i_ID→dept _name
s_ID, dept _name→i_ID
在第8.3.3节中,我们认为函数依赖“i_ID dept_ name”导致了dept advisor模式不属于BCNF。注意,这里α=i ID,β=dept name,和β-α=dept_ name。既然函数依赖项s_ID、dept name→s_ID保存在dept _advisor上,那么属性dept name就包含在一个候选键中,因此,dept _advisor是在3 NF中。
我们已经看到了BCNF和3 NF之间必须做出的权衡,因为没有可靠的BCNF设计。在8.5.4部分中对这些权衡进行了更详细的描述。
8.3.5高等范式
使用功能依赖来分解模式可能不足以避免在某些情况下不必要的重复信息。
考虑一下教师实体设置的一个细微的变化,在这个定义中,我们与每个教师记录一组孩子的名字和一组电话号码。电话号码可能由多个人共享。因此,phone_ number和 child_ name:将是多值属性,根据我们从E-R设计生成模式的规则,我们将有两个模式,一个用于每个多值属性、phone_ number和 child _name::
(ID, child_ name)
(ID, phone _number)
如果我们要将这些模式组合起来
(ID, child_ name, phone_ number)
我们会发现结果是在BCNF中,因为只有一些无关紧要的函数依赖项。因此,我们可能会认为这样的组合是一个好主意。然而,这样的组合是一个坏主意,我们可以通过考虑一个有两个孩子和两个电话号码的教员的例子来了解。例如,让ID 99999的教师有两个孩子,分别叫“大卫”和“威廉”和两个电话号码,512-555-1234和512-555-4321。
在组合模式中,我们必须对每个依赖项重复一次电话号码:
(99999, David, 512-555-1234)
(99999, David, 512-555-4321)
(99999, William, 512-555-1234)
(99999, William, 512-555-4321)
如果我们不重复电话号码,只存储第一个和最后一个元组,我们就会记录下依赖项和电话号码,但是结果的元组意味着David与512-555-1234相对应,而William则对应于512-555-4321。如我们所知,这是不正确的。
因为基于功能依赖的普通表单不足以处理这样的情况,其他依赖项和普通表单已经被定义。我们在8.6和8.7中涵盖了这些。