一个关系是一个二维表,但是一个表并不一定是关系。
关系要成为表必须满足某些约束:
² 表中的每一格必须是单值的属性。重复的组和数组都不能作为值。
² 每一列(属性)的所有条目都必须是同一类型的。
² 每一列都有唯一的名字,列在表中的顺序并不重要。
² 表中任意两行不能相同,行在表中的顺序并不重要。
满足上述约束的二维表就是关系。
函数依赖是属性(列)之间的一种关系。对于学生这个实体来说,知道“学号”这个属性,就可以得出“姓名”、“性别”、“年龄”等一些列属性值。所以我们可以说:“姓名”函数依赖于“学号”,或者“学号”决定“姓名”。而且(“姓名”,“性别”)也是函数依赖于“学号”,等等。
图A(属性之间的函数依赖关系)
如果A ? B (即A决定B),A ? C,那么可以得到 A ? (B,C);而如果(A,B) ? C,却不一定得到 A ? C和B ? C的结果。
关键字是由一个或多个属性组成的,可以唯一标识一行的属性组。在图A中,由于“学号”?(“姓名”,“性别”,“年龄”),所以在这个关系中“学号”可以唯一标识一行,“学号”是这个关系的关键字。
再以学生选课的关系为例:
学号 |
教授 |
课程 |
100 |
王刚 |
数学 |
200 |
赵峒 |
化学 |
100 |
刘明 |
语文 |
112 |
黄均 |
美术 |
图B(选课关系)
上述的业务规则如下:(数据库的关系、属性、是否规范都与业务规则有关,如果没有业务规则,规范化无从谈起)
² 每个学生可以选择多门课程。
² 每个老师只教授一门课程。
² 一门课程可以由多个老师教授。
按照上面的规则,“学号”不能再唯一的标识一行(因为一个学生可以选多门课程,即“学号”不能决定“课程”。假设学号是100,科目却可以是数学,也可以是语文,并不是唯一确定的。),所以“学号”不能成为关键字。但是,(“学号”,“老师”)这个属性组可以唯一的确定“课程”,可以唯一的标识一行(例如学号100的学生选择王刚教授的数学课,因为王刚只能教授数学,所以学号和教授两列能唯一确定课程这一列的值)。所以(“学号”,“老师”)是这个关系的关键字。
但是,假如业务规则的第二条是每个老师可以上多门课的话,(“学号”,“老师”)就不能做关键字。因为加入王刚教授数学和语文两门课,元组(100,王刚,数学),(100,王刚、语文)都是合理的,即学号为100的学生同时选了王刚的两门课。在这个关系中(“100”,“王刚”)不能唯一的确定一条记录了,所以不是关键字。
对于某些关系,改变数据可能导致不希望的后果,称作“更新异常”。这种异常可以通过把关系重新定义为两个或者多个关系来消除。
再以图B为例,如果删除学生100,那么我们将丢失老师王刚教授数学课这一事实。这是删除异常。这个异常意味着在删除了一个实体的某一事实的过程中,却把另外一个实体的某一事实也给删除了(没有计划删除)。
此外,假定另有一个老师魏国教授音乐课,但是在没有学生选他的课以前,我们不能把这个事实---“魏国可以教授音乐课”添加到关系之中。这就是添加异常。这个异常意味着我们要添加一个实体的某一事实之前,我们必须先添加另外一个实体的一个事实。
为了解决这个更新异常,我们可以把这个关系分解成两个关系。
100 |
王刚 |
200 |
赵峒 |
100 |
刘明 |
112 |
黄均 |
图 C(学生选择老师的关系)
王刚 |
数学 |
赵峒 |
化学 |
刘明 |
语文 |
黄均 |
美术 |
图D(老师授课的关系)
通过把图B的关系分解成图C和D的两个关系,消除了上述的更新异常。但是,随之而来的问题是,一个学生选择了一个老师的授课,这个老师却没有在D中出现,是否可以这样?
这个问题的答案在于用户需求,即业务规则之中。如果不允许这样做,就可以用数据库的关联参照完整性约束来解决。如果数据库没有提供支持,则必须用程序来解决。
规范化的本质:每个规范化的关系只有一个主题,如果某个关系有两个或多个主体,它就应该被分解为多个关系,每个关系只能有一个主题。但是,我们再分解关系的时候我们应该建立对关联约束的要求,这一过程就是规范化的实质。
再谈图B的关系,按照业务规则,图B的关系包含了两个主题:第一是学生选课,第二是老师授课。其实造成图B关系更新异常的根源是它包含一个只包含关键字一部分的依赖关系。
图D (属性对关键字的部分函数依赖)
首先是在这里(“学生”,“老师”)是关键字,因为这个组合可以唯一确定“课程”。而(“学生”,“课程”)却不是关键字,因为一门课程可以由多个老师教授。知道(“学生”,“课程”),不能确定是到底是哪个老师任教。
按照关系的定义,关键字可以唯一决定其它属性,但是这里的属性课程,却对作为关键字一部分的教师有依赖关系。即 “教师”?“课程” ----- “教师”决定“课程”。这是根据业务规则:“一个老师只能教授一门课程”来的。(所以设计数据库的过程在需求分析结果和体系结构等业务规则比较清楚以后才可以进行)
这个问题导致了第二范式的定义:
如果一个关系的所有非关键字属性都依赖于整个关键字,那么该关系就属于第二范式。
图B的关系不满足第二范式。因为非关键字属性“课程”依赖于关键字的一部门“教师”,而不是依赖于整个关键字。
而第一范式很简单:
任何符合关系定义的关系都在第一范式之中。
第二范式中的关系也有异常。
仍然考察以前的例子,只是改变它的业务规则,如下。
100 |
王刚 |
数学 |
200 |
赵峒 |
化学 |
100 |
刘明 |
语文 |
112 |
黄均 |
美术 |
图 E (学生选课关系)
业务规则如下:(只改第一条)
² 每个学生只能选择一门。
² 每个老师只教授一门课程。
² 一门课程可以由多个老师教授。
根据这个业务规则,知道了学号,就可以查出该学生选的课程,即“学号”?“老师”;而且知道了学号就可以查出上该生所选的课程,有“学号”?“课程”,所以该关系满足第二范式。但是,如果删除学号为100的学生,或者新增选课记录,仍然会发生和前面相同的异常。
造成这一问题的根源是:根据上面的业务规则,“课程”函数依赖于“老师”。这样就出现一个“传递依赖”,即“学生”?“老师”,“老师”?“课程”。既“老师”对“学生”有函数依赖关系,“课程”对“老师”有函数依赖关系。这是一个传递依赖。要想从第二范式中消除异常,就必须消除传递依赖。
这样就出现了第三范式:一个关系如果在第二范式中,而且没有传递依赖,则该关系就在第三范式中。
要消除这个异常,方法与前面一样,分解关系为图C和图D中的两个关系。
但是就算在第三范式中,也能有异常。再论选课关系:
学号 |
教师 |
课程 |
学分 |
100 |
王刚 |
数学 |
3 |
200 |
赵峒 |
化学 |
2 |
100 |
刘明 |
语文 |
3 |
112 |
黄均 |
美术 |
1 |
113 |
李凯 |
美术 |
1 |
图F (选课关系)
业务规则如下:
² 每个学生可以选一门或者多门课程。
² 每个老师只能教授一门课程。
² 一门课程可以由多个教师任教。
由于一个学生可以选择多门课程,所以“学号”不能决定“课程”,即学号100可以对应很多课程。此外,选了几门课程,就有不同的几个老师,所以“学号”不能决定“老师”,即学号100可能对应很多老师。因此“学号”不能做关键字。
因为(“学号”,“老师”)组合可以决定“课程”,所以也可以决定学分。(“学号”,“课程”)组合决定“老师”,这样,两个组合都可以成为关键字。两个或多个可作关键字的组合称为候选关键字,任意一个选作关键字的候选关键字称作主关键字。显然,“课程”不是候选关键字,它无法决定“学生”
假设我们选(“学号”,“课程”)作主关键字。上述关系满足第一范式,因为满足关系定义;满足第二范式,因为所有非关键字属性都依赖于整个关键字;满足第三范式,因为没有传递依赖(因为教师和学分没有依赖关系)。
但是还是存在更新异常。该问题的根源:“课程”是一个决定因素,但是不再候选关键字中。
Boyce-Codd范式:如果一个关系的每个决定因素都是候选关键字,则该关系在BCNF中。
(未完待续)