《数据库系统概念》学习笔记——第八章 关系数据库设计
Table of Contents
第八章 关系数据库设计
8.1 好的关系设计的特点
8.1.1 设计选择:更大的模式
例如,使用inst_dept(ID, name, salary, dept_name, building, budget)来替代instructor和department。
这样会存在以下问题:
- 信息冗余,维护一致性比较困难
- 无法表示一部分信息和概念,例如:一个新的department,但是没有教师。
8.1.2 设计选择:更小的模式
函数依赖(function dependency): dept_name --> budget
有损分解(lossy decomposition)
无损分解(lossless decomposition)
8.2 原子域和第一范式
一个域是原子的(atomic),如果该域的元素被认为是不可分的单元。
第一范式(First Normal Form, 1NF): 当一个关系模式R的所有属性的域都是原子的,那么这个关系模式满足第一范式。
例如:名字的集合是非原子的;组合属性如address也具有非原子域。
在许多含有复杂结构的实体域中,强制使用第一范式会给应用程序员造成不必要的负担,他必须编码将数据转换成原子形式,转换过程增加了额外的开销。因此支持非原子性的值是非常必要的。
8.3 使用函数依赖进行分解
我们用r(R)表示关系r,其属性集为R。K代表关系r(R)的超码。
8.3.1 码和函数依赖
合法实例(legal instance):满足所有这些现实世界的约束的关系实例是一个合法实例。
函数依赖让我们可以表达唯一标识某些属性的值的约束。考虑一个关系某事r(R),令α⊆R且或β⊆R,
- 给定r(R)的一个实例,这个实例满足(satisfy)函数依赖α -> β的条件是:若实例中所以有元组对t1和t2,若t1[α]=t2[α],则t1[β]=t2[β]
- 如果在r(R)的每个合法实例中都满足函数依赖α -> β,则我们说该函数依赖在模式r(R)上成立(hold)。
我们将以两种方式使用函数依赖:
- 判定关系的实例是否满足给定函数依赖集F
- 说明合法关系集上的约束。因此,我们将只关心满足给定函数依赖集的那些关系实例。如果只考虑模式R上满足函数依赖集F的关系,则说F在r(R)上成立。
在所有关系中都满足的函数依赖是平凡的(trivial)函数依赖。
函数依赖集F的闭包(closure)代表能够从给定F集合中推导出的所有函数依赖的集合,表示为F+。
8.3.2 Boyce-Codd范式 (BCNF)
具有函数依赖集F的关系模式R属于BCNF的条件是:对于F+中所有形如α -> β的函数依赖(其中α⊆R且或β⊆R),下面一条至少成立:
- α -> β是平凡的函数依赖(即:β⊆α)
- α是关系模式R的一个超码
例如:department(dept_name, building, budget) 是满足BCNF的,因为多有成立的非平凡函数依赖,例如:
dept_name-> building, budget中,dept_name是department的一个超码。
分解不属于BCNF的模式的一般规则:
设R为不属于BCNF的一个模式,则存在至少一个非平凡的函数依赖α -> β,其中α不是R的超码。我们在设计中用一下两个模式取代R:
- (α∪β)
- (R-(β-α))
例如:在上面的inst_dept(ID, name, salary, dept_name, building, budget)中存在
ID->name, salary, dept_name
dept_name->building, budget
其中ID是inst_dept的主码
此时α=dept_name, β={building, budget},此时inst_dept可被取代为:
- (α∪β) = {dept_name, building, budget}
- (R-(β-α)) = (ID, name, dept_name, salary)
在这个例子中 β-α = β
8.3.3 BCNF和保持依赖
每次数据库更新时,检查这些一致性约束的开销都很大。
如果函数依赖的检验只需要考虑一个关系就可以完成,那么检查这种约束的开销就很低。
BCNF的分解有时会妨碍对某些函数依赖的高效检查。
例如:一个教师只能与一个系关联,且一个学生可以有多个导师,但是一个给定的系中至多一位。
dept_advisor(s_ID, i_ID, dept_name)存在约束:
s_ID, dept_name -> i_ID
i_ID->dept_name
此时第二个约束不满足BCNF,因为i_ID不是超码,根据BCNF分解规则,得到
- (s_ID, i_ID)
- (i_ID, dept_name)
上面的两个模式都属于BCNF,但是这两个关系模式都无法包含函数依赖s_ID, dept_name -> i_ID中出现的所有属性。
====> 称这个设计不是保持依赖的(dependency preserving)。
8.3.4 第三范式
第三范式的要求比BCNF更宽松一些。
具有函数依赖集F的关系模式R属于第三范式(third normal form)的条件是:对于F+中所有形如α -> β的函数依赖(其中α⊆R且或β⊆R),下面一条至少成立:
- α -> β是平凡的函数依赖(即:β⊆α)
- α是关系模式R的一个超码
- β-α中的每个属性A都包含于R的一个候选码中
注意:满足BCNF的关系模式一定会满足第三范式。
例如:8.3.3中的案例,dept_advisor(s_ID, i_ID, dept_name)存在约束:
s_ID, dept_name -> i_ID
i_ID->dept_name
此时dept_advisor是不满足BCNF的,但是它是满足3NF的。
这是因为: dept_name - i_ID = dept_name 并且dept_name属于主码(s_ID, dept_name)的一部分,所以满足3NF。
8.3.5 更高的范式
在某些情况下,使用函数依赖分解模式可能不足以避免不必要的信息重复,因此,定义了另外的依赖和范式。见8.6节和8.7节。
8.4 函数依赖理论
8.4.1 函数依赖集的闭包
逻辑蕴含(logically imply):给定模式上的函数依赖集F,我们可以证明某些其他的函数依赖在该关系模式上也成立。我们称这些函数依赖被F逻辑蕴含。
- 给定关系r(R), 如果r(R)上的每个满足F的实例也满足f,则R上的函数依赖f被r上的函数依赖集F所逻辑蕴含。
- 例如: F={A->B, B->C} 那么 f=A->C被F逻辑蕴含
闭包(closure):令F是一个函数依赖集,F的闭包是被F逻辑蕴含的所有函数依赖的集合,记作F+。
Armstrong公理(Armstrong's axiom): 提供了寻找逻辑蕴含的三条规则
令(α, β, γ,...)表示属性集,每个字母代表单个属性;用αβ表示α∪β
- 自反律(reflexivity rule):若α为一属性集,且β⊆α, 则α -> β成立
- 增补律(augmentation rule):若α -> β成立且 γ为一属性集,则 αγ -> βγ
- 传递率(transitivity rule):若α -> β,β -> γ成立,则α -> γ成立
为了进一步简化计算,另外还有一些规则。这些规则可以通过Armstrong公理证明。
- 合并律(union rule):若α -> β,β -> γ成立,则αβ -> γ成立
- 分解律(decomposition):若α -> βγ成立,则α -> β,α -> γ成立
- 伪传递律(pseudotransitivity rule):若α -> β 和 βγ ->θ 成立,则 αγ -> θ成立
图8-7形式化的给出了利用Armstrong公里计算F+的过程。
8.4.2 属性集的闭包
如果α -> B,则称属性B被α函数确定(funcitonally determine)。
令α是一个属性集,我们将函数依赖集F下被α函数所确定的所有属性的集合称为F下α的闭包,记作α+。
计算属性闭包的算法:
例如:计算R=(A, B,C,G,H,I), F={A->B, A->C, CG->H, CG->I, B->H}中(AG)+。开始时result = AG,repeat循环执行:
- 第一次repeat:
- 第二次repeat,无新的属性加入result,循环结束
经证明,最坏的情况下,该算法执行时间为F集合规模的二次方。
属性闭包算法的多种用途:
- 判断α是否为超码:可以计算α+,检查α+是否包含R中所有的属性
- 通过检查是否β⊆α+,可以判断函数依赖α->β是否成立。
- 该算法提供了我们另一种计算F+的方法:对于任意γ⊆R, 我们找出闭包γ+;对于任意的S⊆γ+,我们输出一个函数依赖γ->S
8.4.3 正则覆盖
如果去除函数依赖中的一个属性不改变该函数依赖集的闭包,则称该属性是无关的(extroneous)。
无关属性(extroneous attribute)的形式化定义:考虑函数依赖集F及F中的函数依赖α->β。
- 如果A∈α,并且F逻辑蕴含(F-{α->β}})∪ {(α-A)->β},则属性A在α中无关的。
- 如果A∈β,并且(F-{α->β}})∪ {α->(β-A)}逻辑蕴含F,则属性A在β中无关的。
例如:F中有AB->C和A->C,那么B在AB->C中是无关的;F中有AB->CD和A->C,那么C在AB->CD中是无关的。
如何检验一个属性是否无关?
令R为一个关系模式,且F是R上的函数依赖集。考虑α->β的一个属性A:
- 如果A∈β,为了检验A是否是无关的,考虑集合
F' = (F-{α->β}})∪ {α->(β-A)}s
并检验α->A是否能够由F‘推出。为此,计算F’下的α闭包;如果α+包括A,那么A在β中是无关的。
- 如果A∈α,为了检验A是否是无关的,令γ=α-{A},并且检查γ->β是否可以由F推出。为此,计算F下的γ+;如果γ+包含β中的所有属性,则A在α中是无关的。
F的正则覆盖(canonical cover)是Fc是一个依赖集,F逻辑覆盖Fc中的所有依赖,并且Fc也逻辑蕴含F中的所有依赖。此外,Fc必须具有以下性质:
- Fc中任何函数依赖都不含无关属性
- Fc中函数依赖的左半部分都是唯一的。即:Fc中不存在两个依赖α1->β1,α2->β2满足α1=α2。
注意:在图8-9中,如果一个依赖的右半部分只包含一个属性,如:A->C,并且这个属性是无关的。那么我们将得到一个右半部分为空的函数依赖,这样的函数依赖应该被删除。
注意:正则覆盖Fc和F具有相同的闭包!Fc是最小的——它不含无关属性,并且合并了具有相同左半部分的函数依赖。
例如:R=(A, B,C)上有函数依赖F={A->BC, A->B, B->C, AB->C}
- 合并具有相同左半部分的依赖 A->BC, A->B===> A->BC
- 对于AB->C,其中A是无关的,因为F逻辑蕴含 {F-(AB->C)} ∪ (B->C)成立,所以==> B->C
- C在A->BC中是无关的,因为A->BC,被A->B和B->C覆盖
===> {A->B, B->C}
注意:正则覆盖未必是唯一的!
8.4.4 无损分解
令r(R)是一个关系模式,F为r(R)上的函数依赖集。令R1和R2为R的分解。如果用两个关系模式r(R1)和r(R2)替代r(R)时没有信息损失,则我们称该分解是无损分解(lossless decomposition)。
无损分解满足:
不是无损分解的分解为有损分解(lossy decomposition).
用函数依赖描述无损分解:
令R1,R2,R和F如上。如果一下函数依赖至少有一个属于F+,那么R1和R2是R的无损分解:
- R1∩R2 -> R1
- R1∩R2 -> R2
即:R1∩R2是R1或者R2的超码,那么它们是R上的无损分解。
例如:inst_dept(ID, name, dept_name, salary, building budget)
==>instructor(ID, dept_name, name, salary), department(dept_name, building, budget)是无损分解
8.4.5 保持依赖
令F为R上的一个函数依赖集,R1,R2,...,Rn为R的一个分解。F在R上的限定(restriction)是F+中所有只包含Ri中属性的函数依赖的集合Fi。
例如:F={A->B, B->C},计算F在AC上的限定={A->C}
保持依赖的分解(dependency-preserving decomposition):
令F‘=F1∪F2∪...∪Fn,Fi为Ri对应的限定,如果F'+ = F+,那么我们称这是一个保持依赖的分解。
下面的算法可以用来验证保持依赖性,但是代价很大(指数时间)。
另外两种验证保持依赖的方法:
- 方法1:一个保持依赖的充分条件:
- 如果F中的每一个函数依赖都可以在分解得到的某一个关系上验证,那么这个分解是保持依赖的。
- 但是,F中也可能存在一个依赖,它无法在分解后的任意一个关系上验证。
- 方法2:该验证方法对F中的每一个α->β使用下面的过程:(多项式时间)
这里的属性闭包是函数依赖集F下的。如果result包含β中的所有属性,则函数依赖α->β保持。
一个分解是保持依赖的 <=充要条件=>当且仅当F中的所有依赖都保持上述过程
该方法的思想:
- 验证F中每一个函数依赖α->β,看它是否在F’中保持。为此,我们计算F'下的α闭包,当该闭包包含β时,该依赖得以保持。该分解是保持依赖的 ==当且仅当===F中的所有函数依赖都保持
- 使用修改后的属性闭包算法计算F'下的闭包。该方法避免了计算F‘的闭包,减少了计算量。因为F’是Fi的并集,而Fi是F在Ri上的限定。该算法计算出F上的属性闭包,并与R相交,然后将结果属性集加入result,这一系列步骤等价于计算Fi下的result闭包。repeat for Ri得到F‘下的result闭包。
8.5 分解算法
8.5.1 BCNF分解
8.5.1.1 BCNF的判定方法
- 判定方法一:在某些情况下,判定一个关系是否满足BCNF做如下简化:
- 为了检查非平凡的函数依赖α->β是否违反BCNF,计算α+,并验证它是否包含R中的所有属性,即:验证α是R的超码
- 检查关系模式R是否属于BCNF,仅需检查给定集合F中的函数依赖是否违反BCNF就可以了,不用检查F+中的所有函数依赖。
但是,当一个关系分解后,后一步过程就不再适用。
- 判定方法二:为了检查分解后的关系Ri是否属于BCNF,可以进行如下判定
- 对于Ri中属性的每个子集α,确保α+要么不包含Ri-α的任何属性,要么包含Ri的所有属性。
如果Ri存在某个属性集α违反BCNF,则α->(α+ - α) ∩ R出现在F+中。
8.5.1.2 BCNF分解算法
下面给出了BCNF分解算法,用这个算法得到的BCNF分解,还是一个无损分解。
验证:用(Ri-β)和(α,β)取代Ri时,依赖α->β成立,并且(Ri-β) ∩(α,β)=α
这个算法所需时间为指数级别,因为算法中检查一个Ri是否属于BCNF的代价是指数级别。
举例: class(course_id, title, dept_name, credits, sec_id, semester, year, building, room_number, capacity, time_slot_id ) 存在
course_id -> title, dept_name, credits
building, room_number -> capacity
course_id, sec_id, semester, year -> building, room_number, time_slot_id
则,计算BCNF分解:
- 函数依赖course_id -> title, dept_name, credits成立,但是course_id不是超码,所以class可被分解为
- course(course_id, title, dept_name, credits) - 属于BCNF
- class-1(course_id, sec_id, semester, year, building, room_number, capacity, time_slot_id)
- 函数依赖building, room_number -> capacity,building和room_number不是class-1的候选码,所以class-1可被分解为:
- classroom(building, room_number, capacity) - 属于BCNF
- section(course_id, sec_id, semester, year, building, room_number, time_slot_id) - 属于BCNF
于是==> 分解得到course,classroom,section,它们都属于BCNF。
8.5.2 3NF分解
图8-12给出了将模式转为3NF的保持依赖且无损的算法。其中Fc是F的正则覆盖。
运算时间为多项式时间,
例如:dept_advisor(s_ID, i_ID, dept_name), 其函数依赖集为s_ID, dept_name->i_ID, i_ID -> dept_name
在执行下述算法的过程中,
- 得到R1(i_ID, dept_name), R2(s_ID, i_ID, dept_name),
- 由于R2包含R的候选码,所以不再继续创建关系模式
- 由于R1包含在R2中,所以删除R1
可以看出,这个算法会删除被其他关系模式所包含的模式。
有时3NF算法的结果不仅是3NF,也是BCNF。说明该算法可以作为另一种求解BCNF的算法。
- 首先使用3NF计算出分解
- 然后对于分解中任何不属于BCNF 的模式,用BCNF进行分解。如果结果不是保持依赖的,则还原到3NF。
8.5.3 3NF算法的正确性
3NF如何保证无损分解?
3NF通过保留至少有一个模式包含被分解模式的候选码,确保该分解是一个无损分解。
3NF的结果不是唯一确定的,因为Fc不是唯一的。并且某些情况下,算法的结果还与Fc中函数依赖的顺序有关。
8.5.4 BCNF和3NF的比较
3NF的优缺点:
- 优点:总是可以再满足无损并保持依赖的前提下得到3NF
- 缺点:可能不得不用空值来表示数据项间的某种可能有意义的联系,并且存在信息重复的问题。
使用函数依赖进行数据库设计的目标是:
- BCNF
- 无损
- 保持依赖
由于不是总能达到BCNF,所以需要在BCNF和3NF中做个权衡和选择。
通常来讲,再不能得到保持依赖的BCNF时,我们倾向于使用3NF。因为SQL检查非主码约束的函数依赖很困难。
- 目前没有数据库支持强制实施函数依赖的所需的复杂断言
- 可以再分解不能保持依赖时,使用物化视图上的主码约束对其进行检查,以降低开销
- 若一个非保持依赖的BCNF分解,存在α->β,利用物化视图存储αβ,并令其主码为α。进而可以在物化视图上维护该函数依赖。
- 缺点:会有时间和空间的额外开销;目前大多数数据库系统不支持物化视图上的约束
- 优点:程序员不需要额外的工作来维护冗余数据上的一致性
8.6 使用多值依赖的分解
有些关系模式虽然是BCNF,但从某种意义上也存在信息重复的问题。例如:多值属性。
多值依赖、第四范式(Fourth Normal Form)。
引用第四范式处理多值依赖,它比BCNF的约束更严格。
属于4NF的一定属于BCNF ,属于BCNF的不一定是4NF。
8.6.1 多值依赖
函数依赖A->B成立,那么不能有两个元组在A上的值相同,B上的值不同。多值依赖允许这种情况发生。
因此,函数依赖有时也称为相等产生依赖(equality-generating dependency),而多值依赖也称为元组产生依赖(tuple-generating dependency)。
多值依赖multivalued dependency:
令r(R)为一个关系模式,并令α⊆R且β⊆R,那么多值依赖可表示为:α ->-> β。
多值依赖在R上成立的条件是:在关系r(R)上的任意合法实例中,对于r中任意一对满足t1[α]=t2[α]的元组t1和t2,r中都存在元组t3和t4,使得:
与函数依赖相同,将以两种方式使用多值依赖:
1, 检查关系以确定它们在给定的函数依赖集和多值依赖集下是否合法
2. 在合法关系集上指定约束,我们希望只考虑满足给定函数依赖和多值依赖的集合
令D表示多值依赖和函数依赖的集合。D的闭包D+是由D逻辑蕴含的所有函数依赖和多值依赖的集合。
根据多值依赖的定义,可以利用如下规则推导依赖集,对于α⊆R,β⊆R:
- 若α->β,则α->->β。每个函数依赖也是一个多值依赖
- 若α->->β,则α->->R-α-β
8.6.2 第四范式
8.6.3 4NF分解
8.7 更多的范式
投影-连接范式(Project-Join Normal Form, PJNF,第五范式):
- 连接依赖join dependency:更一般的概化了多值依赖
域-码范式(Domain-key Normal Form, DKNF):处理更一般化的额约束
其缺点是:难易推导,且没有完备和有效的推导规则,使用很少。
8.8 数据库设计过程
本节介绍规范化是如何融合在整体数据库设计过程中的。
8.3节至今,讲述了如何对给定关系模式r(R)进行规范化。
我们可以采用以下几种方式得到模式r(R):
- E-R图生成
- 利用规范化将r(R)分解为更小的模式
- r(R)是一个即席设计的结果,然后检验其是否满足一个期望的范式
8.8.1 E-R模型和规范化
函数依赖有助于我们检测到不好的E-R设计。
规范化可以留给数据库设计者在E-R建模时靠直觉实现,也可以从E-R模型生成的关系模式上规范的进行,二者可以任选其一。
创建E-R设计的过程倾向于产生4NF设计。
如果一个多值依赖成立且不是被相应的函数依赖所隐含的,那么它通常由以下情况引起:
- 一个多对多的联系集
- 实体集的一个多值属性
对于多对多的联系集,每个相关的实体集都有自己的模式,并且存在一个额外的模式用于表示联系集。
对于多值属性,会创建一个单独的模式,包含该属性以及实体集的主码。
8.8.2 属性和联系的命名
随着模式变得变得更大,联系集的数量的不断增加,对属性、联系和实体一致的命名方式会使得数据库管理者更加轻松。常用的规则包括:
- 数据库设计的一个期望特性是唯一角色假设(unique-role assumption): 即:每个属性名在数据库中只有唯一的含义。这使得我们不能在不同的模式中使用同一个属性来表示不同的东西。
- 如果不同关系中的属性有相同的含义,则可以使用相同的名字进行表示。
- 模式中的属性的顺序无关紧要,然而管把主码放在前面
- 联系集常常以相关试题集名称的拼接来命名,可能使用连字符或下划线连接,例如:inst_sec。
- 实体集的命名通常采用单数,但不绝对,只要系统中保持一致就好。
8.8.3 为了性能去规范化
有时候为了提高性能,数据库设计者会选择包含冗余信息的模式,即没有规范化。其代价是需要一些额外的工作来保持冗余数据的一致性。
例如:查询课程的所有先修课程,需要连接course和prereq
- 一个不计算连接的方法 是保存一个包含course和prereq的所有属性的关系==> 查询更快,但是具有冗余信息,维护起来更复杂。
去规范化(denormalization):把一个规范化的模式变成非规范化的操作。
- 另外一种方法是,使用规范化的模式,但是将course和prereq的连接作为物化视图存储==> 会带来一些时间和空间上的开销,但是数据库可以自行维护更新。
8.8.4 其他设计问题
数据库设计中有一些方面不能用规范化描述,可能会导致不好的数据库设计。例如与时间段有关的数据就容易存在这样的问题。
例如:存储不同年份中每个系的教师总数
- 设计1:total_inst(dept_name, year, size),为的函数依赖时dept_name, year->size,属于BCNF
- 设计2:建立total_inst_2007, total_inst_2008,....,==>虽然这些也属于BCNF,但是这样关系太多,查询起来比较费劲
- 设计3:建立单独的dept_year(dept_name, total_inst_2007, total_inst_2008, total_inst_2009...) ==> 关系需要经常变化,查询起来会比较费劲
像设计3这种的表示方法,属性的每一个值一列,叫做交叉表(crosstab)。虽然它很有用,但是在数据库的设计中并不可取。
8.9 时态数据模型
时态数据(temporal data)是具有关联的时间段的数据,在时间段之间有效(valid)。
数据的快照(snapshot)表示一个特定的时间点上该数据的值。
时态函数依赖(temporal functional dependency):在某个特定时间点上成立的函数依赖称为时态函数依赖。形式化的说:下图的公式在关系模式r(R)上成立的条件是,对于r(R)的所有合法实例,r的所有快照都满足函数依赖X->Y.
时态数据库的设计方法:
- 方法1:把起始时间和终止时间作为每个这样的关系添加有效时间信息。
- 例如:course(course_id, title, dept_name, start, end)
- 如果另一个关系有一个参照时态关系的外码,数据库设计者必须决定参照是否针对当前版本的数据还是一个特定时间点的数据。在后一种情况下,参照关系必须也记录时间信息。
一个时态关系中的原始主码无法再唯一的表示一条元组。为了解决这个问题,我们可以在主码中添加起始和终止时间。但是也存在一些问题:
- 有可能存在存储时间段存在重合的数据,该问题主码约束无法捕获。如果系统能够支持自带的有效时间类型,它就能检测和避免这种重叠的时间段
- 为了指明这种参照关系的外码,参照元组必须包含起始和终止时间属性为它们的外码的一部分,其值也必须与被参照元组相匹配。同时,更新也必须得到传递。
- 方法2:如果所有对时态数据的参照都仅仅指向当前数据,更简单的方法是不在关系中添加时间信息,而为过去的值创建一个history关系,history中可以包含start_time和end_time。
总结
- 本章给出了数据库设计中易犯的错误,和怎样系统的设计数据库模式以避免这样的错误。这些错误包括:信息重复和不能表示某些信息
- 我们给出了从E-R设计到关系数据库设计的发展过程,什么时候可以安全的合并模式,什么时候应该分解模式。所有有效的分解都必须是无损的。
- 原子域和第一范式
- 函数依赖,BCNF,3NF
- 如果分解是保持依赖的,则给定一个数据库更新,所有的函数依赖都可以由单个的关系进行验证,而无需计算分解后的关系的连接。
- 我们展示了如何用函数依赖进行推导。我们着重讲了什么依赖是一个依赖集逻辑蕴含的。我们还定义了正则覆盖的概念,它是与给定函数依赖集等价的最小的函数依赖集。
- 我们概述了一个将关系分解为BCNF的算法;有一些关系 不存在保持依赖的 BCNF分解
- 我们用正则覆盖分解成3NF,它比BCNF的条件更弱一些。属于3NF的关系也许会有冗余,但是总存在 保持依赖的 3NF分解
- 我们介绍了多值依赖的概念,它指明仅用函数依赖无法指明的约束。我们用多值依赖定义了第四范式(4NF)。
- 其他的范式,如PJNF和DKNF,消除了更多更细微的冗余。但是其操作困难且很少使用
- 我们之所以可以对关系数据库设计你定义严格的方法,是因为关系数据库模型建立在严谨的数学基础上,这是关系模型相对于其他数据模型的主要优势之一。