第七章:§7 Relational Database Design

§7.1 Features of Good Relational Designs

关系模式的设计如果直接根据 E-R 图来导出,那么,E-R 图的好坏就决定了关系模式的好坏。

假如一开始,我们设计了一个这样的关系模式:

$in_dep(ID, name, salary, dept_name, building, budget)$

这看上去很不错,因为它是 instructordepartment 的自然连接,有许多查询可以用更少的 JOIN 来实现。但是仔细想想,这造成了冗余的问题。

一方面,部门的 building 和 budget 这些本来在 department 表中存在的内容需要被重复存储,可能会存在冗余的问题;另一方面,假如创建了一个新的部门,没有老师入职,那么在 in_dep 中表示不了这个部门。

§7.1.1 Decomposition

对于有冗余的关系模式,就需要分解它们。当然,如果过于极端,每个关系模式都只有一个属性,那就表示不了任何关系。不过考虑下面这个没那么极端的例子:

$employee(ID, name, street, city, salary)$

我们可以把它分解为下面的两个模式,毕竟,分解这种东西,想分就分嘛:

$employee1(ID, name)$

$employee2(name, street, city, salary)$

我的理解是,分解就是 JOIN 的逆操作。

看看,这个分解有一个问题,问题出现在两个员工同名的情形。假如两个不同的个体具有同一个名字,那么,这样分解的两个模式在 JOIN 的时候会出现错误的混合这种情形。还是费些笔墨写个例子吧:

$(57766, Kim, Main, Perryridge, 75000)$

$(98776, Kim, North, Hampton, 67000)$

分解之后,得到

$(57766, Kim)$

$(98776, Kim)$

以及

$(Kim, Main, Perryridge, 75000)$

$(Kim, North, Hampton, 67000)$

把这两个分解 JOIN 起来,就会得到 4 个合并的元组,而不是原先的两个。这样的分解,就叫做有损分解,反之,叫无损分解

更多的元组,却表示了更少的信息,蕴含了信息量的理论。

§7.1.2 无损分解

令 $R$ 是一个关系模式,$R_1$ 和 $R_2$ 是 $R$ 的一个分解。也就是说,从属性的角度来看,$R = R_1 + R_2$。如果是一个无损分解,那么,把 $R$ 用 $R_1$ 和 $R_2$ 来替换,不会有信息的损失。

什么时候有信息的损失呢?也就是对于一个关系实例 $r(R)$,我们必须使用 $r_1(R_1)$ 和 $r_2(R_2)$ 来表示它,而不能只使用 $r_(R)$。为什么会这样,就是因为 JOIN 的时候出现了错误的混合。

更准确地,如果是无损连接分解的,那么对所有合法的实例,关系 $r$ 包含和下列 SQL 语句相同的元组:

1
2
3
4
SELECT *
FROM (SELECT R1 FROM r)
NATURAL JOIN
(SELECT R2 from r)

回到上面的例子,employee 的分解就是有损的,因为原来的关系实例 $r$ 并不等于上面查询的结果,而是上面结果的子集。

为什么更多的元组会有更少的信息呢?因为关系模式需要表示关系。分解之后的两个关系模式产生了更多的元组,因而无法正确地表示关系。这一联系丧失了,则意味着信息量少了。

§7.1.3 Normalization Theory

现在我们就可以看一看什么是好的一个关系模式了。一个好的关系模式应该不存在冗余信息。

设计一个好的关系模式需要用到正则化技术,它氛围两步走:(1) 判断当前的关系模式是不是一种好的模式(可以存在很多种好的模式);(2) 如果当前的模式不是一个好的模式,则把它分解为一些小的关系模式,使得达到一个范式。分解必须是无损的。

为了确定关系模式是否是范式,需要用到现实世界的额外信息。最常见的手段是函数依赖

§7.2 Decomposition Using Functional Dependencies

§7.2.1 Notational Conventions

略。

§7.2.2 Keys and Functional Dependencies

现实生活中最常见的约束可以用键或者函数依赖来表示。之前我们讨论过键,这里来看看扩展了的键——函数依赖。

之前我们说,一组属性能够成为键,在于它可以确定一个元组。现在,对函数依赖来说,它有两个组成部分,形如 $\alpha \to \beta$。$\alpha$ 是一个属性集,它可以唯一确定 $\beta$。

我们说,如果一个实例的所有元组对都有 $t_1[\alpha] = t_2[\alpha] \implies t_1[\beta] = t_2[\beta]$,则这个实例满足函数依赖 $\alpha \to \beta$。

如果一个模式的所有合法实例都满足 $\alpha \to \beta$ 的话,就说这个模式满足这个函数依赖。

理解这个点,只需要区分两个概念:实例和模式。前者是后者的一个实例化,后者是前者的抽象。

函数依赖的作用有两点:

  1. 测试某些关系是否满足给定的函数依赖 $F$;
  2. 在合法关系上添加约束后,只需要关注具有函数依赖的那些关系,其他的不需要考虑。

有些函数依赖是平凡的,比如 $A \to A$、$AB \to A$。

怎么读函数依赖呢?我通常是这样,比如对于函数依赖 $A \to B$,读作:$A$ 一样的话,$B$ 也一样。

对于一个函数依赖集 $F$ 来说,有可能推断出一些 $F$ 中没有的函数依赖成立。我们把所有能够由 $F$ 推断出来的函数依赖集记作 $F^+$。$F^+$ 包含了 F 中的所有函数依赖,定义为 $F$ 的闭包。

§7.2.3 Lossless Decomposition and Functional Dependencies

用函数依赖可以判断一个分解是否是无损的。具体来说,如果一个 $R$ 的分解 $R_1$ 和 $R_2$ 是无损的,则在 $F^+$ 中至少有以下一个条件满足:

  • $R_1 \cap R_2 \to R_1$
  • $R_1 \cap R_2 \to R_2$

换句话说,也就是 $R_1$ 和 $R_2$ 的交集必须成为 $R_1$ 或 $R_2$ 的主键。

这是很好理解的,因为,只有交集是主键,才能确保在 JOIN 的时候不存在混合连接的问题。

比如上面的例子,交集 name 就不能成为任何一个分解后的模式的主键,则说明分解是有损的。

技术上说,在关系模式上需要施加两个约束:

  • $R_1 \cap R_2$ 是 $r_1$ 的主键;
  • $R_1 \cap R_2$ 在 $r_2$ 中要引用 $r_1$,形成外键引用。

当然,这个判断方法只是充分的,不是必要的。这涉及到多值依赖的问题,后续会再讨论。

§7.3 Normal Forms

这里介绍两个范式:BCNF 和 3NF。

§7.3.1 Boyce-Codd Normal Form

一个我们最喜欢的范式是 BCNF,它基于函数依赖理论去除了所有的冗余。

§7.3.1.1 Definition

一个关系模式 $R$ 关于一个函数依赖集 $F$ 满足 BCNF,如果对于 $F^+$ 中所有的函数依赖,至少以下一个成立:

  1. 这个函数依赖是平凡的;
  2. 函数依赖左侧的条件是模式 $R$ 的超码。

换句话说,对于任何一个非平凡的函数依赖,箭头左侧的内容应该是这个模式的超码,可以确定模式的元组。


那么,如果一个模式不是 BCNF 的,如何转变为 BCNF 呢?

很简单,如果一个模式不是 BCNF,则至少存在一个非平凡的函数依赖 $\alpha \to \beta$,其中 $\alpha$ 不是模式 $R$ 的超码。

那么,新创建一个关系模式,由 $\alpha \cup \beta$ 组成。然后,在原来的关系模式中删除 $\beta$,但是要保留 $\alpha$。

举个例子,在 $in_dep(ID, name, salary, dept_name, building, budget)$ 中,存在两个函数依赖:$ID \to name, dept_name, salary$ 和 $dept_name \to building, budget$。

显然 ID 是个超码,但是 dept_name 不是。所以,把 dept_name,building,budget 提取出来,创建一个新的关系。把老关系去掉箭头右侧的部分,保留 dept_name。

这样,形成两个新的关系模式:

  • $ID, name, salary, dept_name$;
  • $dept_name, building, budget$。

之所以要强调,保留 dept_name,是因为有时候,箭头右侧会包含箭头左侧的内容。如果进行第一次 BCNF 分解后,得到的模式还不满足 BCNF,则需要进一步分解。

§7.3.1.2 BCNF and Dependency Preservation

我们之前讲过,数据库要检查许多约束。如果这种约束的检查在一个关系上就可以完成,肯定是很高效的事情。然而,BCNF 可能会阻碍这一目的。

举个例子。原来,一个学生只能选一个导师。现在改了,一个导师只能隶属于某个学院,而学生可以选许多导师,但是在一个学院只能选一个导师。

现在,可以使用一个三元组来表示这一关系:

$dept_advisor(s_ID, i
_ID, dept_name)$

根据上面的文字描述,在这一模式上需要满足两个函数依赖:

  1. $i_ID \to dept_name$
  2. $s_ID, dept_name \to i_ID$

这两个函数依赖中,第一个的 $i_ID$ 并不是超码,所以需要分解,最终得到以下两个新的模式:

$i_ID, dept_name$

$s_ID, i_ID$

这两个模式是符合 BCNF 的,但是上文的第二个函数依赖无法在任何一个模式中被检查,只能通过把两个模式并起来才能检查。

由于这种数据库设计必须通过 JOIN 才能检查函数依赖,所以这种设计被叫做依赖保持的。

因为以来保持非常好,所以我们需要啊考虑另一种比 BCNF 弱一些的范式,它允许依赖保持。

§7.3.2 Third Normal Form

3NF 只比 BCNF 多了一个条件,不过为了严谨,还是把 3NF 需要的所有条件列出来,新增的条件是最后一条:

  1. $\alpha \to \beta$ 是平凡的函数依赖;
  2. $\alpha$ 是 $R$ 的超码;
  3. 在 $\beta - \alpha$ 的每一个属性 $A$ 被包含在 $R$ 的候选码中。

注意,第三点并不是要求一个候选码包含所有的 $\beta - \alpha$ 的属性。这些属性可以被不同的候选码包含,但是至少要被一个候选码包含。

还是看看之前的例子:

  1. $i_ID \to dept_name$
  2. $s_ID, dept_name \to i_ID$

我们之前说到,第一个函数依赖的存在导致数据库模式不满足 BCNF。不过,这里,$dept_name - i_ID$ 后得到的 $dept_name$ 在候选码中,因而符合 3NF 的第三条。

§7.3.3 Comparison of BCNF and 3NF

3NF 相较于 BCNF 有一个很大的优势,在于依赖保持。但它也有不足,也就是有时候,我们必须用空值来表示重复的信息。

总的来说,设计数据库要达成的 3 个目标是:

  1. BCNF
  2. 无损
  3. 依赖保持

然而,由于开销的原因,市面上绝大多数数据库系统都没有提供除了 key 之外的其他函数依赖约束的设计。总而言之,理论是理论,实操目前不太好实现。(反正我是不会实现,已被数据库内核劝退)

§7.3.4 Higher Normal Forms

有时候,用函数依赖理论分解模式的时候,无法避免冗余的信息。比如,考虑一个教授可以有许多小孩,也可以有许多座机电话。那么,这两个多值属性就需要分别用两个模式来表示。

$(ID, child_name)$

$(ID, phone_number)$

然而,其实也可以合在一起:

$(ID, child_name, phone_number)$

我们可以验证,合并后的结果满足 BCNF,因为其上没有函数依赖约束。然而,这个想法并不是很好,因为如果教授有两个小孩,两个座机号码,则需要从 $2 \times 2 = 4$ 条记录来存储。

$$
(123, A, 6666)
(123, A, 8888)
(123, B, 6666)
(123, B, 8888)
$$

当然,你也可以选择其中的两条来记录,如第一条和最后一条,这样不存放冗余信息。然而,这样可能会导致一些错误的蕴含,因为事实上,A 也可以和 8888 关联,但是只选取两条记录可能丢失了这种关联。

对于这种依赖,会在 7.6 和 7.7 讨论。

§7.4 Funtional-Dependency Theory

为了验证一个数据库设计是否满足某些范式,了解函数依赖理论是很必要的。比如,在 BCNF 和 3NF 的定义中,都要求对 $F^+$ 中的所有函数依赖,满足一些性质。然而,函数依赖的闭包,如何计算呢?接下来就会讨论这些问题。

§7.4.1 Closure of a Set of Functional Dependencies

对于一个函数依赖集 $F$,我们可以证明在 $F$ 外的一些函数依赖也在模式上成立。当我们验证范式的时候,不能只考虑 $F$ 中哦共的,而是考虑所有($F^+$)中的函数依赖。

函数依赖集 $F$ 的闭包 $F^+$ 指的是所有通过 $F$ 能推理得到的函数依赖的集合。

以下的几个推理规则可以加快计算速度,它们叫做 Armstrong 公理:

  • 自反律 如果 $\alpha$ 是一个属性集合并且 $\beta \subseteq \alpha$,则 $\alpha \to \beta$ 成立。
  • 增广律 如果 $\alpha \to \beta$ 成立,并且 $\gamma$ 是一个属性集合,则 $\gamma\alpha \to \gamma\beta$ 成立。
  • 传递律 如果 $\alpha \to \beta$ 成立且 $\beta \to \gamma$ 成立,则 $\alpha \to \gamma$ 成立。

Armstrong 公理是正确且完备的。通过它可以推导以下 3 个推论:

  • 合并律 如果 $\alpha \to \beta$ 成立且 $\alpha \to \gamma$ 成立,那么 $\alpha \to \beta\gamma$ 成立。
  • 分解律 如果 $\alpha \to \beta\gamma$ 成立,那么 $\alpha \to \beta$ 成立,$\alpha \to \gamma$ 也成立。
  • 伪传递律 如果 $\alpha \to \beta$ 成立且 $\gamma\beta \to \delta$ 成立,那么 $\alpha\gamma \to \delta$ 也成立。

上面的三个推论都可以通过基本的 Armstrong 公理来证明,这里赘述一下。

合并律的证明只需要在两个依赖上运用增广律,得到 $\beta\gamma$ 和 $\alpha\beta$ 的形式,再运用一次传递律。

分解律对箭头右侧的两个属性运用自反律,然后运用传递律。

伪传递律使用一次增广律,然后运用传递律即可。

下面展示一个找 $F^+$ 的算法:

1
2
3
4
5
6
7
8
9
10
F+ = F
应用自反律 // 找到所有平凡依赖
重复
对每个 F+ 中的函数依赖 F
对 f 增广律
把得到的新函数依赖添加到 F+ 中
对每对 F+ 中的函数依赖 f1 和 f2
如果 f1 和 f2 可以用传递律合并
则把得到的结果添加到 F+ 中
直到 F+ 不改变

当然,这种算法很可能非常冗长。7.4.2 介绍另一种算法。

§7.4.2 Closure of Attribute Sets

属性集的闭包指的是,某个属性 $\alpha$ 通过函数依赖能够确定的属性集合,记作 $\alpha^+$。

算法如下:

1
2
3
4
5
result := α
重复
对每个 F 中的函数依赖 β → γ
如果 β ⊆ result 则 result := result ∪ γ
直到 result 不改变

属性闭包有如下的用处:

  1. 测试 $\alpha$ 是不是超码;
  2. 判断 $\alpha \to \beta$ 是否成立
  3. 另一种计算 $F^+$ 的方式。

§7.4.3 Canonical Cover

为了花费更小的开销验证函数依赖集 $F$,需要用到正则覆盖这个概念。引入这一概念之前,先定义什么是冗余属性。

冗余属性有两种可能,从左侧删除属性和从右侧删除属性。


先讨论从左侧删除,这一操作会让新的函数依赖集更强。因为原来需要 $m$ 个属性才能确定等式右侧,现在只需要 $m - 1$ 个。

既然函数依赖集更强了,有必要考察,到底被删除的属性是不是冗余的。换言之,删除前比较弱的函数依赖集能否蕴含新的函数依赖集呢?如果可以,则说明属性是冗余的。


从右侧删除道理类似,这一操作会让新的属性集更弱。因为,原来能推出 $m$ 个属性,现在只能推出 $m - 1$ 个了。

原来的函数依赖集更强,那么就需要考察下新的函数依赖集能否蕴含原来的出来。


道理我都懂,但是,怎么判断这种蕴含关系呢?事实上,其实不用查看整个函数依赖集的蕴含关系,毕竟我们只对集合进行了一个属性的删除,只需要考察和这一删除相关的函数依赖是否能被蕴含。

就从左侧删除而言,新的依赖集更强。那么,考察旧的依赖集能否推出新的依赖集。不过,其实只需要在乎一个函数依赖能否被推出。哪一个呢?是让新的依赖集更强的那个,也就是 $(\alpha - A) \to \beta$。

总结一下,我们要验证上述的函数依赖在的函数依赖集($F$)中是否能蕴含得到。怎么理解这种强的关系呢?弱的函数依赖集需要 $\alpha \to \beta$ 才能确定 $\beta$,但是新的函数依赖集可以去掉 $\alpha$ 中 $A$ 这一条件。

所以,验证左侧删除的属性是否冗余的方法是,在 $F$ 中计算 $(\alpha - A)^+$,考察其是否包含 $\beta$。

从右侧删除则同理,这次是新的函数依赖集($F’$)更弱。反过来想,$F$ 强在哪里呢?强在 $\alpha \to \beta$,因为 $F’$ 只能做到 $\alpha \to (\beta - A)$。不过更具体些,$F$ 强在 $\alpha \to A$。

那么,就需要在 $F’$ 上验证 $\alpha \to A$ 是否成立。

计算正则覆盖的算法如下:

1
2
3
4
5
6
Fc = F
重复
用合并律,使得 a -> b, a -> c 合并为 a -> bc
找到 Fc 中的一个存在冗余属性的函数依赖 a-> b
如果属性是冗余的,则从 Fc 中的 a -> b 中删除之
直到 Fc 不改变

有时候,如果一个函数依赖的某一侧全部是冗余的,那么,可以放心地把整个函数依赖删掉。

如 $A \to B$,$A \to C$, $C \to B$ 这三个函数依赖中,第一个就是多余的,因为在 $F’$ 中,仍然能得到 $A \to B$。

当然,正则覆盖不一定是唯一的,但是各个正则覆盖是等价的,就像极大无关组一样。

§7.4.4 Dependency Preservation

学习函数依赖理论后,就可以更准确地刻画依赖保持这一概念。

先引入一个概念,叫做 $F$ 在 $R_i$ 上的限制:令 $F$ 是一个模式 $R$ 上的函数依赖。$F$ 在 $R_i$ 上的限制指的是所有在 $F^+$ (注意是 $F^+$)中只包含 $R_i$ 中属性的函数依赖的集合。

令 $F’ = F_1 \cup F_2 \cup \cdots \cup F_n$,其中 $F_i$ 是 $F$ 在 $R_i$ 上的限制。如果 $F’^+ = F+$,则这个分解是依赖保持分解

根据定义,一个朴素的算法是这样的:

1
2
3
4
5
6
7
8
计算 F+
对 D 中的每个 R_i
F_i 是 F+ 在 R_i 上的限制
F' = 空集
对每个限制 Fi
F' = F' ∪ Fi
计算 F'+
如果 F'+ = F+ 则返回 true 否则返回 false

然而,因为它要计算 $F^+$,所以开销很大。因此,有以下两个解决方案:

  1. 如果 $F$ 中的每个函数依赖都可以在分解中的某个关系上进行检测,则分解是依赖保持的;
  2. 指数级的算法。

前者不一定每次都能成功,因此我们更关注后者。

算法:

1
2
3
4
5
6
result = a
重复
对分解中的每个 Ri
t = (result ∩ Ri)+ ∩ Ri
result = result ∪ t
直到 result 不改变

换句话说,每一轮都要对每个 $R_i$ 和 result 的共有元素算一下再 $F$ 下的属性闭包,再交上 $R_i$。如果 result 包含了 $\alpha \to \beta$ 中 $\beta$ 的所有属性,则这一条依赖被保持了。

§7.5 Algorithms for Decomposition Using Functional Dependencies

§7.5.1 BCNF Decomposition

前面讲过,对 BCNF 的验证,只需要根据定义就好了。但是,定义需要计算 $F^+$,开销很大。不过好消息是,有更高效的测试方法。

§7.5.1.1 Testing for BCNF

为了验证一个关系模式 $R$ 是否满足 BCNF,可以简化为诶两种情形:

  1. 检查非平凡的函数依赖是否违反了 BCNF,即 $\alpha^+ \neq R$;
  2. 检查一个关系模式 $R$ 是否满足 BCNF,只需要检查所有 $F$ 中的函数依赖即可,不需要检查所有 $F^+$ 的。

这真是一个天大的好消息,然而,第 2 点可能对分解之后的模式不适用。

例:有 $(A, B, C, D, E)$ 上的 $A \to B$ 和 $BC \to D$,分解为 $(A, B)$ 和 $(A, C, D, E)$ 后,看上去后者上没有成立的函数依赖了,但是在 $F^+$ 中却存在 $AC \to D$,令后者不满足 BCNF。

为了解决这一问题,可以应用这一算法:

  • 对 $R_i$ 中属性的每一个子集 $\alpha$,计算 $\alpha^+$ 要么包含 $R_i$ 的所有属性,要么不包含 $R_i$ 的任何属性(除了自己)。

对于前者,说明 $\alpha$ 是个超码,OK。对于后者,说明 $F^+$ 中没有非平凡的函数依赖和 $\alpha$ 有关,也 OK。

§7.5.1.2 BCNF Decomposition Algorithm

BCNF 的分解算法,其实和 7.3.1 节的差不多。这里列出来这个算法,兼听则明:

1
2
3
4
5
6
7
result := R
done := false
当没有 done
如果 有个 R 中的 Ri 不满足 BCNF
令 a -> b 是在 Ri 上成立的非平凡函数依赖,且 a+ 不包含 Ri 且 a ∩ b 是空集
result := (result - Ri) ∪ (Ri - b) ∪ (a, b)
否则 done := true

说人话,就是对于一个关系模式集合,如果其中有一个关系模式上的函数依赖导致这个关系模式不满足 BCNF,则拆分这个关系模式。

拆分的方法是,先把这个模式从 result 关系模式集合中删掉。

之后,$\beta$ 从原模式中剔除(如果 $\beta$ 中有 $\alpha$ 则保留 $\alpha$),形成第一个新模式。另一个新模式是 $(\alpha, \beta)$。

可以验证这个分解是无损的,因为根据无损分解的定义,分解后两个模式的交集 $\alpha$ 能够在 $(\alpha, \beta)$ 上满足 $\alpha \to \beta$。

§7.5.2 3NF Decomposition

算法如下:

1
2
3
4
5
6
7
8
9
10
11
12
令 Fc 是 F 的正则覆盖
i = 0
对 Fc 中的所有函数依赖 a -> b
i++
Ri = ab
如果 Rj 中没有一个包含了 R 的候选键
则 i++
Ri 是 R 的任何一个候选键
重复
如果任何一个 Rj 被包含在了另一个模式 Rk 中
则删除 Rj
直到不能删除更多的 Rj

这个算法的思想是,对每个正则覆盖中的函数依赖,都创建了一个模式,满足了 3NF 的定义。同时,它确保了至少有一个模式包含了候选键,从而确保了是无损分解。

§7.5.3 Correctness of the 3NF Algorithm

略。

§7.6 Decomposition Using Multivalued Dependencies

有时候 BCNF 的约束还不够,比如之前我们讲的教授、座机电话和小孩的例子。这种问题出现在多个多值属性交织在一起的情形。

当然,很自然的避免冗余的想法是,把属性分解。如,原来是 $(ID, phone_number, child)$,分解为 $(ID, phone_number)$ 和 (ID, child) 不就好了?但是,这样的分解并没有理论支撑,所以这里引入多值依赖和第四范式 4NF。

§7.6.1 Multivalued Dependencies

函数依赖的目标是消除一些元组的存在,它也叫平等生成依赖,而多值依赖被称为元组生成依赖

严谨地,令 $r(R)$ 是一个关系模式,$\alpha \subseteq R$ 且 $\beta \subseteq R$,多值依赖 $\alpha \twoheadrightarrow \beta$ 在 $R$ 上成立,
如果对 $r(R)$ 的每个合法实例,对所有的元组对 $t_1$ 和 $t_2$ 使得 $t_1[\alpha] = t_2[\alpha]$,都在 $r$ 中存在 $t_3$ 和 $t_4$,使得

$$
t_1[\alpha] = t_2[\alpha] = t_3[\alpha] = t_4[\alpha]
\
t_3[\beta] = t_1[\beta]
\
t_3[R - \beta] = t_2[ R - \beta]
\
t_4[\beta] = t_2[\beta]
\
t_4[R - \beta] = t_1[ R - \beta]
$$

看起来很复杂,但想要表达的意思是,这一对出现了,和这对属性互补的一对也应该出现。

从多值依赖的定义出发,以下两个规则成立:

  • 函数依赖也是多值依赖;
  • $\alpha \twoheadrightarrow \beta \implies \alpha \twoheadrightarrow R - \alpha - \beta$

§7.6.2 Fourth Normal Form

第四范式和 BCNF 范式的定义非常像,只是把函数依赖换成了多值依赖。如果一个模式在函数依赖和多值依赖集 $D$ 上满足 4NF,则对 $D^+$ 中的所有形如 $\alpha \twoheadrightarrow \beta$ 多值属性:

  • $\alpha \twoheadrightarrow \beta$ 平凡;
  • $\alpha$ 是 $R$ 的超码。

§7.6.3 4NF Decomposition

4NF 分解和 BCNF 的分解算法类似,对于导致不满足 4NF 的多值依赖,进行拆分。不再赘述。

§7.7 More Normal Forms

4NF 绝不是最终的范式,还有 PJNF 和 DKNF 等等,但是这两个因为缺少正确和完备的推理,所以用得少。

2NF 只有历史爱好者才会研究。1NF 将会在下一节提到。

§7.8 Atomic Domains and First Normal Form

第一范式解决的是原子取值域的问题。之前我们讲过多值属性,讲过复合属性,这些属性可以再分,因此不是原子属性,不满足 1NF。

当然了,到底原子与否还是要看数据库是怎么用的。比如,整型可以认为是原子了吧?但要是把每一位当做基本单元,还是非原子的。

有时候,非原子的属性很有用,所以现代数据库也支持一些非原子属性。

§7.9 Database-Design Process

§7.9.1 E-R Model and Normalization

如果 E-R 模型选的好,按理来说应该不需要花太大精力正则化。当然,设计的不好另说。

§7.9.2 Naming of Attributes and Relationships

给属性和关系起名,也有讲究。

§7.9.3 Denormalization for Performance

有时候为了性能,需要反范式化。

§7.9.4 Other Design Issues

有时候,一些涉及到时间的设计,crosstabs 并不是很好的选择。

§7.10 Modeling Temporal Data

涉及到时间的数据,比如,某个数据只在某些时间段内有效,需要一些设计原则。在这种情况下,关系、主键并不是永久成立的,而是在一定时间的条件下才成立。