关系数据理论
问题的提出
前面已经讨论了数据库系统的一般概念,介绍了关系数据库的基本概念、关系模型的三个部分以及关系数据库的标准语言SQL。但是还有一个很基本的问题尚未涉及:针对一个具体问题,应该如何构造一个适合于它的数据库模式,即应该构造几个关系模式,每个关系由哪些属性组成等。这是数据库设计的问题,确切地讲是关系数据库逻辑设计问题。
实际上设计任何一种数据库应用系统,不论是层次的、网状的还是关系的,都会遇到如何构造合适的数据模式即逻辑结构的问题。由于关系模型有严格的数学理论基础,并且可以向别的数据模型转换,因此,人们就以关系模型为背景来讨论这个问题,形成了数据库逻辑设计的一个有力工具——关系数据库的规范化理论。规范化理论虽然是以关系模型为背景,但是它对于一般的数据库逻辑设计同样具有理论上的意义。
下面首先回顾一下关系模型的形式化定义。
在关系数据库中已经讲过,一个关系模式应当是一个五元组。
这里:
◦关系名 \(R\) 是符号化的元组语义。
◦ \(U\) 为一组属性。
◦ \(D\) 为属性组 \(U\) 中的属性所来自的域。
◦ \(\text{DOM}\) 为属性到域的映射。
◦ \(F\) 为属性组 \(U\) 上的一组数据依赖。
由于 \(D\) 、 \(\text{DOM}\) 与模式设计关系不大,因此在本章中把关系模式看作一个三元组:
当且仅当 \(U\) 上的一个关系 \(r\) 满足 \(F\) 时, \(r\) 称为关系模式 \(R\text{<}U,F\text{>}\) 的一个关系。
作为一个二维表,关系要符合一个最基本的条件:每一个分量必须是不可分的数据项。满足了这个条件的关系模式就属于第一范式(1NF)。
在模式设计中,假设己知一个模式 \(S\phi\) ,它仅由单个关系模式组成,问题是要设计一个模式 \(SD\) ,它与 \(S\phi\) 等价,但在某些指定的方面更好一些。这里通过一个例子来说明一个不好的模式会有些什么问题,分析它们产生的原因,并从中找出设计一个好的关系模式的办法。
在举例之前,先非形式地讨论一下数据依赖的概念。
数据依赖是一个关系内部属性与属性之间的一种约束关系。这种约束关系是通过属性间值的相等与否体现出来的数据间相关联系。它是现实世界属性间相互联系的抽象,是数据内在的性质,是语义的体现。
人们已经提出了许多种类型的数据依赖,其中最重要的是函数依赖(Functional Dependency, FD)和多值依赖(Multi-Valued Dependency, MVD)。
函数依赖极为普遍地存在于现实生活中。比如描述一个学生的关系,可以有学号( \(\text{Sno}\) )、姓名( \(\text{Sname}\) )、系名( \(\text{Sdept}\) )等几个属性。由于一个学号只对应一个学生,一个学生只在一个系学习。因而当“学号”值确定之后,学生的姓名及所在系的值也就被唯一 地确定了。属性间的这种依赖关系类似于数学中的函数 \(y=f(x)\) ,自变量 \(x\) 确定之后,相应的函数值 \(y\) 也就唯一地确定了。
类似的有 \(\text{Sname}=f(\text{Sno})\) , \(\text{Sdept}=f(\text{Sno})\) ,即 \(\text{Sno}\) 函数决定 \(\text{Sname}\) , \(\text{Sno}\) 函数决定 \(\text{Sdept}\) ,或者说 \(\text{Sname}\) 和 \(\text{Sdept}\) 函数依赖于 \(\text{Sno}\) ,记作 \(\text{Sno}\rightarrow \text{Sname}\) , \(\text{Sno} \rightarrow \text{Sdept}\) 。
例1:建立一个描述学校教务的数据库,该数据库涉及的对象包括学生的学号( \(\text{Sno}\) )、所在系( \(\text{Sdept}\) )、系主任姓名( \(\text{Mname}\) )、课程号( \(\text{Cno}\) )和成绩( \(\text{Grade}\) )。假设用一个单一的关系模式 \(\text{Student}\) 来表示,则该关系模式的属性集合为
\[ U = \set{ \text{Sno}, \text{Sdept}, \text{Mname}, \text{Cno}, \text{Grade}} \tag{3} \] 现实世界的己知事实(语义)告诉我们:
①一个系有若干学生,但一个学生只属于一个系。
②一个系只有一名(正职)负责人。
③一个学生可以选修多门课程,每门课程有若干学生选修。
④每个学生学习每一门课程有一个成绩。
于是得到属性组U上的一组函数依赖 \(F\) (如图1所示)。
图1 Student上的一组函数依赖
如果只考虑函数依赖这一种数据依赖,可以得到一个描述学生的关系模式 \(\text{Student}\text{<}U,F \text{>} \) 。表1是某一时刻关系模式Student的一个实例,即数据表。
Sno | Sdept | Mname | Cno | Grade |
---|---|---|---|---|
S1 | 计算机系 | 张明 | C1 | 95 |
S2 | 计算机系 | 张明 | C1 | 90 |
S3 | 计算机系 | 张明 | C1 | 88 |
S4 | 计算机系 | 张明 | C1 | 70 |
S5 | 计算机系 | 张明 | C1 | 78 |
⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
表1 Student表
但是,这个关系模式存在以下问题:
(1)数据冗余。比如,每一个系的系主任姓名重复出现,重复次数与该系所有学生的所有课程成绩出 现次数相同,如表1所示。这将浪费大量的存储空间。
(2)更新异常(update anomalies)。由于数据冗余,当更新数据库中的数据时,系统要付出很大的代价来维护数据库的完整性,否则会面临数据不一致的危险。比如,某系更换系主任后,必须修改与该系学生有关的每一个元组。
(3)插入异常(insertion anomalies)。如果一个系刚成立,尚无学生,则无法把这个系及其系主任的信息存入数据库。
(4)删除异常(deletion anomalies)。如果某个系的学生全部毕业了,则在删除该系学生信息的同时,这个系及其系主任的信息也丢掉了。
鉴于存在以上种种问题,可以得出这样的结论:\(\text{Student}\) 关系模式不是一个好的模式。一个好的模式应当不会发生插入异常、删除异常和更新异常,数据冗余应尽可能少。
为什么会发生这些问题呢?
这是因为这个模式中的函数依赖存在某些不好的性质。这正是本章要讨论的问题。假如把这个单一的模式改造一下,分成三个关系模式:
\(\text{S}(\text{Sno},\text{Sdept},\text{Sno}\rightarrow\text{Sdept});\)
\(\text{SC}(\text{Sno},\text{Cno},\text{Grade},(\text{Sno},\text{Cno})\rightarrow \text{Grade});\)
\(\text{DEPT}(\text{Sdept},\text{Mname}, \text{Sdept}\rightarrow \text{Mname})\)
这三个模式都不会发生插入异常、删除异常的问题,数据的冗余也得到了控制。
一个模式的数据依赖会有哪些不好的性质,如何改造一个不好的模式,这就是下一节规范化要讨论的内容。
规范化
本节首先讨论一个关系属性间不同的依赖情况,讨论如何根据属性间依赖情况来判定关系是否具有某些不合适的性质,通常按属性间依赖情况来区分关系规范化程度为第一范式、第二范式、第三范式和第四范式等;然后直观地描述如何将具有不合适性质的关系转换为更合适的形式。
第一节关系模式 \(\text{Student}\text{<}U,F \text{>} \) 中有 \(\text{Sno}\rightarrow \text{Sdept}\) 成立,也就是说在任何时刻 \(\text{Student}\) 的关系实例(即 \(\text{Student}\) 数据表)中,不可能存在两个元组在 \(\text{Sno}\) 上的值相等,而在 \(\text{Sdept}\) 上的值不等。因此,表6.2的 \(\text{Student}\) 表是错误的。因为表中有两个元组在 \(\text{Sno}\) 上都等于 \(\text{S}_1\) ,而 \(\text{Sdept}\) 一个为计算机系,一个为自动化系。
Sno | Sdept | Mname | Cno | Grade |
---|---|---|---|---|
S1 | 计算机系 | 张明 | C1 | 95 |
S1 | 自动化系 | 张明 | C1 | 90 |
S3 | 计算机系 | 张明 | C1 | 88 |
S4 | 计算机系 | 张明 | C1 | 70 |
S5 | 计算机系 | 张明 | C1 | 78 |
⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
表2 一个错误的Student表
函数依赖
定义1:设 \(R(U)\) 是属性集 \(U\) 上的关系模式, \(X,Y\) 是 \(U\) 的子集。若对于 \(R(U)\) 的任意 一个可能的关系 \(r\) , \(r\) 中不可能存在两个元组在 \(X\) 上的属性值相等,而在 \(Y\) 上的属性值不等,则称 \(X\) 函数确定 \(Y\) 或 \(Y\) 函数依赖于 \(X\) ,记作 \(X \rightarrow Y\) 。
函数依赖和别的数据依赖一样是语义范畴的概念,只能根据语义来确定一个函数依赖。例如,姓名一年龄这个函数依赖只有在该部门没有同名人的条件下成立。如果允许有同名人,则年龄就不再函数依赖于姓名了。
设计者也可以对现实世界作强制性规定,例如规定不允许同名人出现,因而使姓名—年龄函数依赖成立。这样当插入某个元组时这个元组上的属性值必须满足规定的函数依赖, 若发现有同名人存在,则拒绝插入该元组。
注意,函数依赖不是指关系模式 \(R\) 的某个或某些关系满足的约束条件,而是指 \(R\) 的一切关系均要满足的约束条件。
下面介绍一些术语和记号。
◦ \(X \rightarrow Y\) ,但 \(Y \nsubseteq X\) ,则称 \(X \rightarrow Y\) 是非平凡的函数依赖。
◦ \(X \rightarrow Y\) ,但 \(Y \subseteq X\) ,则称 \(X \rightarrow Y\) 是平凡的函数依赖。对于任一关系模式,平凡函数依赖都是必然成立的,它不反映新的语义。若不特别声明,总是讨论非平凡的函数依赖。
◦若 \(X \rightarrow Y\) ,则 \(X\) 称为这个函数依赖的决定属性组,也称为决定因素(determinant)。
◦若 \(X \rightarrow Y\) , \(Y \rightarrow X\) ,则记作 \(X \leftarrow \rightarrow Y\) 。
◦若 \(Y\) 不函数依赖于 \(X\) ,则记作 \(X \nrightarrow Y\) 。
定义2:在 \(R(U)\) 中,如果 \(X \rightarrow Y\) ,并且对于 \(X\) 的任何一个真子集 \(X'\) ,都有 \(X' \nrightarrow Y\) ,则称 \(Y\) 对 \(X\) 完全函数依赖,记作
\[ X \xrightarrow{F} Y \tag{5} \]若 \(X \rightarrow Y\) ,但 \(Y\) 不完全函数依赖于 \(X\) ,则称 \(Y\) 对 \(X\) 部分函数依赖(partial functional dependency),记作
\[ X \xrightarrow{P} Y \tag{6} \]例1中 \((\text{Sno},\text{Cno})\xrightarrow{F} \text{Grade}\) 是完全函数依赖, \((\text{Sno},\text{Cno})\xrightarrow{P} \text{Sdept}\) 是部分函数依赖,因为 \(\text{Sno}\rightarrow \text{Sdept}\) 成立,而 \(\text{Sno}\) 是 \((\text{Sno},\text{Cno})\) 的真子集。
定义3:在 \(R(U)\) 中,如果 \(X \rightarrow Y(Y \nsubseteq X),Y \nrightarrow X,Y \rightarrow Z, Z \nsubseteq Y\) 则称 \(Z\) 对 \(X\) 传递函数依赖(transitive functional dependency)。记为 \(X \xrightarrow{传递} Z\) 。这里加上条件 \(Y \nrightarrow X\) 是因为如果 \(Y \rightarrow X\) ,则 \(X \leftarrow \rightarrow Y\) ,实际上是 \(X \xrightarrow{直接} Z\) ,是直接函数依赖而不是传递函数依赖。
例1中 \(\text{Sno}\rightarrow \text{Sdept},\text{Sdept} \rightarrow \text{Mname}\) 成立,所以 \(\text{Sno} \xrightarrow{传递} \text{Mname}\) 。
码
码是关系模式中的一个重要概念。在关系数据库-关系中已给出了有关码的若干定义,这里用函数依赖的概念来定义码。
定义4:设 \(K\) 为 \(R \text{<} U,F \text{>}\) 中的属性或属性组合,若 \(K \xrightarrow{F} U\) ,则 \(K\) 为 \(R\) 的候选码(candidate key)。
注意 \(U\) 是完全函数依赖于 \(K\) ,而不是部分函数依赖于 \(K\) 。如果 \(U\) 部分函数依赖于 \(K\) ,即 \(K \xrightarrow{P} U\) ,则 \(K\) 称为超码(Surpkey)。候选码是最小的超码,即 \(K\) 的任意一个真子集都不是候选码。
若候选码多于一个,则选定其中的一个为主码(primary key)。
包含在任何一个候选码中的属性称为主属性(prime attribute);不包含在任何候选码中的属性称为非主属性(nonprime attribute)或非码属性(non-key attribute)。最简单的情况,单个属性是码;最极端的情况,整个属性组是码,称为全码(all-key)。
在后面的章节中主码或候选码都简称为码。读者可以根据上下文加以识别。
例2:关系模式 \(\text{S}(\underline{\text{Sno}},\text{Sdept},\text{Sage})\) 中单个属性 \(\text{Sno}\) 是码,用下划线显示出来。 \(\text{SC}(\underline{\text{Sno},\text{Cno}},\text{Grade})\) 中属性组合 \((\text{Sno},\text{Cno})\) 是码。
例3:关系模式 \(R(\underline{P,W,A})\) 中,属性 \(P\) 表示演奏者, \(W\) 表示作品, \(A\) 表示听众。假设一个演奏者可以演奏多个作品,某一作品可被多个演奏者演奏,听众也可以欣赏不同演奏者的不同作品,这个关系模式的码为 \((P,W,A)\) ,即全码。
定义5:关系模式 \(R\) 中属性或属性组 \(X\) 并非 \(R\) 的码,但 \(X\) 是另一个关模式的码,则称 \(X\) 是 \(R\) 的外部码(foreignkey),也称外码。
如在 \(\text{SC}(\underline{\text{Sno},\text{Cno}},\text{Grade})\) , \(\text{Sno}\) 不是码,但 \(\text{Sno}\) 是关系模式 \(\text{S}(\underline{\text{Sno}},\text{Sdept},\text{Sage})\) 的码,则 \(\text{Sno}\) 是关系模式 \(\text{SC}\) 的外码。
主码与外码提供了一个表示关系间联系的手段,如例2中关系模式 \(\text{S}\) 与 \(\text{SC}\) 的联系就是通过 \(\text{Sno}\) 来体现的。
范式
关系数据库中的关系是要满足一定要求的,满足不同程度要求的为不同范式。满足最低要求的叫第一范式,简称1NF;在第一范式中满足进一步要求的为第二范式,其余以此类推。有关范式理论的研究主要是E. F. Codd做的工作。1971—1972年Codd系统地提出了1NF、2NF、3NF的概念,讨论了规范化的问题。1974年,Codd和Boyce共同提出了一个新范式,即BCNF。1976年Fagin提出了4NF。后来又有研究人员提出了5NF。
所谓“第几范式”原本是表示关系的某一种级别,所以常称某一关系模式 \(K\) 为第几范式。现在则把范式这个概念理解成符合某一种级别的关系模式的集合,即 \(R\) 为第几范式就可以写成 \(R \in x \text{NF}\) 。
对于各种范式之间的关系有
成立,如图2所示。
图2 各种范式之间的关系
一个低一级范式的关系模式通过模式分解(schema decomposition)可以转换为若干个高一级范式的关系模式的集合,这种过程就叫规范化(normalization)。
1NF
满足了每一个分量必须是不可分的数据项的关系模式就属于第一范式(1NF)。
2NF
定义6:若 \(R \in 1 \text{NF}\) ,且每一个非主属性完全函数依赖于任何一个候选码,则 \(R \in 2 \text{NF}\) 。下面举一个不是 \(2 \text{NF}\) 的例子。
例4:有关系模式 \(\text{S-L-C}(\text{Sno},\text{Sdept},\text{Sloc},\text{Cno},\text{Grade})\) ,其中 \(\text{Sloc}\) 为学生的住处, 并且每个系的学生住在同一个地方。 \(\text{S-L-C}\) 的码为 \((\text{Sno},\text{Cno})\) 。则函数依赖有
\((\text{Sno},\text{Cno})\xrightarrow{F} \text{Grade}\)
\(\text{Sno}\rightarrow \text{Sdept},(\text{Sno},\text{Cno})\xrightarrow{P} \text{Grade}\)
\(\text{Sno}\rightarrow \text{Sloc},(\text{Sno},\text{Cno})\xrightarrow{P} \text{Sloc}\)
\( \text{Sdept} \rightarrow \text{Sloc}\) (每个系的学生只住一个地方)
函数依赖关系如图3所示。
图3 函数依赖示例
图中用虚线表示部分函数依赖。另外 \(\text{Sdept}\) 还函数确定 \(\text{Sloc}\) ,这一点在讨论第二范式时暂不考虑。
可以看到非主属性 \(\text{Sdept}、\text{Sloc}\) 并不完全函数依赖于码。因此 \(\text{S-L-C}(\text{Sno},\text{Sdept},\text{Sloc},\text{Cno},\text{Grade})\) 不符合 \(2\text{NF}\) 定义,即 \(\text{S-L-C} \notin 2 \text{NF}\) 。
一个关系模式 \(R\) 不属于 \(2 \text{NF}\) ,就会产生以下几个问题:
(1)插入异常。假若要插入一个学生 \(\text{Sno}=\text{S7},\text{Sdept}=\text{PHY},\text{Sloc}=\text{BLD2}\) ,但该生还未选课,即这个学生无 \(\text{Cno}\) ,这样的元组就插不进 \(\text{S-L-C}\) 中。因为插入元组时必须给定码值,而这时码值的一部分为空,因而学生的固有信息无法插入。
(2)删除异常。假定某个学生只选一门课,如 \(\text{S4}\) 就选了一门课 \(\text{C3}\) ,现在 \(\text{C3}\) 这门课他也不选了,那么 \(\text{C3}\) 这个数据项就要删除。而 \(\text{C3}\) 是主属性,删除了 \(\text{C3}\) ,整个元组就必须一起删除,使得 \(\text{S4}\) 的其他信息也被删除了,从而造成删除异常,即不应删除的信息也删除了。
(3)修改复杂。某个学生从数学系(\(\text{MA}\))转到计算机科学系(\(\text{CS}\)),这本来只需修改此学生元组中的 \(\text{Sdept}\) 分量即可,但因为关系模式 \(\text{S-L-C}\) 中还含有系的住处 \(\text{Sloc}\) 属性,学 生转系将同时改变住处,因而还必须修改元组中的 \(\text{Sloc}\) 分量。另外,如果这个学生选修了 \(k\) 门课, \(\text{Sdept}、\text{Sloc}\) 重复存储了 \(4\) 次,不仅存储冗余度大,而且必须无遗漏地修改后个元组中全部 \(\text{Sdept}、\text{Sloc}\) 信息,造成修改的复杂化。
分析上面的例子可以发现问题在于有两类非主属性,一类如 \(\text{Grade}\) ,它对码是完全函数依赖;另一类如 \(\text{Sdept}、\text{Sloc}\) ,它们对码不是完全函数依赖。解决的办法是用投影分解把关系模式 \(\text{S-L-C}\) 分解为两个关系模式:\(\text{SC(Sno,Cno,Grade)}\) 和 \(\text{S-L(Sno,Sdept,Sloc)}\) 。
关系模式 \(\text{SC}\) 与 \(\text{S-L}\) 中属性间的函数依赖可以用图4、图5表示如下。
图4 SC中的函数依赖
图5 S-L中的函数依赖
关系模式 \(\text{SC}\) 的码为 \((\text{Sno},\text{Cno})\) ,关系模式 \(\text{S-L}\) 的码为 \(\text{Sno}\) ,这样就使得非主属性对码都是完全函数依赖了。
3NF
定义7:设关系模式 \(R \text{<} U,F \text{>} \in 1 \text{NF}\) ,若 \(R\) 中不存在这样的码 \(X\) ,属性组 \(Y\) 及非主属性 \(Z(Z \nsupseteq Y)\)使得 \(X \rightarrow Y, Y \rightarrow Z\) 成立, \(Y \nrightarrow X\) ,则称 \(R \text{<} U,F \text{>} \in 3 \text{NF}\) 。
由定义7可以证明,若 \(R \in 3 \text{NF}\) ,则每一个非主属性既不传递依赖于码,也不部分依赖于码。也就是说,可以证明如果 \(R\) 属于 \(3 \text{NF}\) ,则必有 \(R\) 属于 \(2 \text{NF}\) 。
在图4中关系模式 \(\text{SC}\) 没有传递依赖,而图5中关系模式 \(\text{S-L}\) 存在非主属性对码的传递依赖。在 \(\text{S-L}\) 中,由 \(\text{Sno} \rightarrow \text{Sdept}(\text{Sdept} \nrightarrow \text{Sno})\) , \(\text{Sdept} \rightarrow \text{Sloc}\) ,可得 \(\text{Sno}\rightarrow \text{Sloc}\) 。因此 \(\text{SC} \in 3 \text{NF}\) ,而 \( \text{S-L} \notin 3 \text{NF}\) 。
一个关系模式 \(R\) 若不是 \(3 \text{NF}\) ,就会产生2NF相类似的问题。解决的办法同样是将 \(\text{S-L}\) 分解为:\(\text{S-D(Sno,Sdept)}\) 和 \(\text{D-L(Sdept,Sloc)}\) 。分解后的关系模式 \(\text{S-D}\) 与 \(\text{D-L}\) 中不再存在传递依赖。
BCNF
\(\text{BCNF}\) (Boyce Codd Normal Form)是由Boyce与Codd提出的,比上述的 \(\text{3NF}\) 又进了一步,通常认为 \(\text{BCNF}\) 是修正的第三范式,有时也称为扩充的第三范式。
定义8:关系模式 \(R \text{<} U,F \text{>} \in 1 \text{NF}\) ,若 \(X \rightarrow Y\) 且 \(Y \nsubseteq X\) 时 \(X\) 必含有码,则 \(R \text{<} U,F \text{>} \in \text{BCNF}\) 。
也就是说,关系模式 \(R \text{<} U,F \text{>}\) 中,若每一个决定因素都包含码,则 \(R \text{<} U,F \text{>} \in \text{BCNF}\) 。
由 \(\text{BCNF}\) 的定义可以得到结论,一个满足 \(\text{BCNF}\) 的关系模式有:
◦所有非主属性对每一个码都是完全函数依赖。
◦所有主属性对每一个不包含它的码也是完全函数依赖。
◦没有任何属性完全函数依赖于非码的任何一组属性。
由于 \(R \in \text{BCNF}\) ,按定义排除了任何属性对码的传递依赖与部分依赖,所以 \(R \in \text{3NF}\) 。 但是若 \(R \in \text{3NF}\) , \(R\) 未必属于 \(\text{BCNF}\) 。
下面用几个例子说明属于 \(\text{3NF}\) 的关系模式有的属于 \(\text{BCNF}\) ,但有的不属于 \(\text{BCNF}\) 。
例5:考察关系模式 \(\text{C(Cno, Cname, Pcno)}\) ,它只有一个码 \(\text{Cno}\) ,这里没有任何属性对 \(\text{Cno}\) 部分依赖或传递依赖,所以 \(\text{C} \in \text{3NF}\) 。同时 \(\text{C}\) 中 \(\text{Cno}\) 是唯一的决定因素,所以 \(\text{C} \in \text{BCNF}\) 。对于关系模式 \(\text{C(Sno, Cno, Grade)}\) 可作同样分析。
例6:关系模式 \(\text{S(Sno, Sname, Sdept, Sage)}\) ,假定 \(\text{Sname}\) 也具有唯一性,那么 \(\text{S}\) 就有两个码,这两个码都由单个属性组成,彼此不相交。其他属性不存在对码的传递依赖与部分依赖,所以 \(\text{S} \in \text{3NF}\) 。同时 \(\text{S}\) 中除 \(\text{Sno}、\text{Sname}\) 外没有其他决定因素,所以 \(\text{S}\) 也属于 \(\text{BCNF}\) 。
以下再举几个例子。
例7:关系模式 \(\text{SJP(S, J, P)}\) 中,\(\text{S}\) 是学生, \(\text{J}\) 表示课程, \(\text{P}\) 表示名次。每一个学生选修每门课程的成绩有一定的名次,每门课程中每一名次只有一个学生(即没有并列名次)。由语义可得到下面的函数依赖:
\[ \text{(S,J)} \rightarrow \text{P}\quad (\text{J},\text{P}) \rightarrow S \tag{8} \]所以 \(\text{(S,J)}\) 与 \(\text{(J,P)}\) 都可以作为候选码。这两个码各由两个属性组成,而且它们是相交的。这个关系模式中显然没有属性对码传递依赖或部分依赖。所以 \(\text{SJP} \in \text{3NF}\) ,而且除 \(\text{(S,J)}\) 与 \(\text{(J,P)}\) 以外没有其他决定因素,所以 \(\text{SJP} \in \text{BCNF}\) 。
例8:关系模式 \(\text{STJ(S, T, J)}\) 中, \(\text{S}\) 表示学生, \(\text{T}\) 表示教师, \(\text{J}\) 表示课程。每一教师只教一门课,每门课有若干教师,某一学生选定某门课,就对应一个固定的教师。由语义可得到如下的函数依赖。
\[ \text{(S,J)} \rightarrow \text{T}, \text{(S,T)} \rightarrow \text{J}, \text{T} \rightarrow \text{J} \tag{9} \]函数依赖关系可以用图6.6表示,这里 \(\text{(S,J)}、\text{(S,T)}\) 都是候选码。
图6 STJ中的函数依赖
\(\text{STJ}\) 是 \(\text{3NF}\) ,因为没有任何非主属性对码传递依赖或部分依赖,但 \(\text{STJ}\) 不是 \(\text{BCNF}\) 关系,因为 \(\text{T}\) 是决定因素,而 \(\text{T}\) 不包含码。
对于不是 \(\text{BCNF}\) 的关系模式,仍然存在不合适的地方。非 \(\text{BCNF}\) 的关系模式也可以通过分解成为 \(\text{BCNF}\) 。例如 \(\text{STJ}\) 可分解为 \(\text{ST(S,T)}\) 与 \(\text{TJ(T,J)}\) ,它们都是 \(\text{BCNF}\) 。
\(\text{3NF}\) 和 \(\text{BCNF}\) 是在函数依赖的条件下对模式分解所能达到的分离程度的测度。一个模式中的关系模式如果都属于 \(\text{BCNF}\) ,那么在函数依赖范畴内它已实现了彻底的分离,己消除了插入和删除的异常。 \(\text{3NF}\) 的“不彻底”性表现在可能存在主属性对码的部分依赖和传递依赖。