在上一节( 关系数据模型公式) 里,我们定义了关系模型的数学概念。 现在我们知道如何用关系数据模型存储数据, 但是我们还不知道如何从这些表里面检索某些东西。 比如,某人想知道销售部件'Screw' 的所有的供应商的名称。 而就此我们可以定义两种差别相当大的表示对关系的操作的符号:
关系演算是一种演算的符号, 其中的查询是通过向关系附加特定的操作符来表示的。
关系微积分是一种逻辑符号, 其中的查询是通过公式来表示的。 这些公式代表答案里的元组必须满足的某些逻辑约束。
关系演算 是 E. F. Codd 于 1972 首先提出的。它包括一个对表进行操作的集合:
SELECT (σ):从关系里面抽取出满足给定限制条件的 元组。 令 R 为一个表,包含一个属性 A。 σA=a(R) = {t ∈ R ∣ t(A) = a} 这里 t 表示 R 的一条元组而 t(A) 表示元组 t 的属性A 的值。
PROJECT (π):从一个关系里面抽取指明的 属性(列)。 令 R 为一个包含一个属性 X 的关系。 πX(R) = {t(X) ∣ t ∈ R},这里 t(X) 表示元组 t 里的属性 X 的值。
PRODUCT (×):计算两个关系的笛卡儿积。 令 R 为含有 k1 个元的表而令 S 为含有 k2 个元的表。 R × S 是所有含有 k1 + k2个元的元组的集合, 其前面 k1 个元构成 R 里的一条元组而后面 k2 个元构成 S 里的一条元组。
UNION (∪):计算两个表集合理论上的并集。 给出表 R 和 S (两者有相同元/列数), R ∪ S 的联合就是所有在 R里面有或 S 里面有或在两个表里面都有的元组的集合。
INTERSECT (∩):计算两个表集合理论上的交集。给出表 R 和 S, R ∩ S 是同时在 R 和 S里面的元组的集合。 我们同样要求 R 和 S 拥有相同的元/列数。
DIFFERENCE (− 或 ∖):计算两个表的异(区别)的集合。 令 R 和 S 还是拥有相同元/列的表。 R - S 是在 R 里面但是不在 S 里面的元组的集合。
JOIN (∏):通过共同属性联接两个表。 令 R 为一个有属性 A,B 和 C 的表,而令 S 为一个有属性 C,D 和 E的表。 两个表有一个共同的属性,属性 C。 R ∏ S = πR.A,R.B,R.C,S.D,S.E(σR.C=S.C(R × S))。 我们在这里干了什么呢?首先我们计算笛卡儿积 R × S。 然后我们选择那些公共属性 C 的数值相同 (σR.C = S.C)的元组。 这时我们拥有一个包含属性 C 两次的表, 然后我们把重复的列去掉以后得出我们要的结果。
Example 1-2. 一个内部联接
让我们看看计算一个连接需要的各个步骤产生的表。给出下面两个表:
R: S: A | B | C C | D | E ---+---+--- ---+---+--- 1 | 2 | 3 3 | a | b 4 | 5 | 6 6 | c | d 7 | 8 | 9
首先我们先计算笛卡儿乘积 R × S 并得到:
R x S: A | B | R.C | S.C | D | E ---+---+-----+-----+---+--- 1 | 2 | 3 | 3 | a | b 1 | 2 | 3 | 6 | c | d 4 | 5 | 6 | 3 | a | b 4 | 5 | 6 | 6 | c | d 7 | 8 | 9 | 3 | a | b 7 | 8 | 9 | 6 | c | d
在选择 σR.C=S.C(R × S) 后我们得到:
A | B | R.C | S.C | D | E ---+---+-----+-----+---+--- 1 | 2 | 3 | 3 | a | b 4 | 5 | 6 | 6 | c | d
删除重复的列 S.C 我们用下面的操作: πR.A,R.B,R.C,S.D,S.E(σR.C=S.C(R × S)) 将其选出,得到:
A | B | C | D | E ---+---+---+---+--- 1 | 2 | 3 | a | b 4 | 5 | 6 | c | d
DIVIDE (÷)令 R 是一个有属性 A,B,C,和 D 的表,并且令 S 是一个有着属性 C 和 D 的表。 然后我们把除定义为:
R ÷ S = {t ∣ ∀ ts ∈ S ∃ tr ∈ R令 tr(A,B)=t∧tr(C,D)=ts} 这里 tr(x,y) 代表表 R 里只包含元素 x 和 y的元组。 注意元组t 只包含关系 R 里的元素 A 和 B。
给出下面的表
R: S: A | B | C | D C | D ---+---+---+--- ---+--- a | b | c | d c | d a | b | e | f e | f b | c | e | f e | d | c | d e | d | e | f a | b | d | eR ÷ S 生成
A | B ---+--- a | b e | d
关于关系演算更详细的描述和定义,请参考 [Ullman, 1988] 或 [Date, 1994]。
Example 1-3. 使用关系演算的一个例子
回忆一下我们阐述的所有可以用于从数据库里检索数据的关系操作符。 现在我们可以回到前一章的例子里 (关系数据模型里的操作), 那时我们需要知道销售部件 Screw 的所有供应商的名称。 现在我们可以用下面的关系演算操作来回答这个问题:
πSUPPLIER.SNAME(σPART.PNAME='Screw'(SUPPLIER ∏ SELLS ∏ PART))
我们把这样的操作称做一次查询。如果我们对我们的例子表 (供应商和部件数据库) 进行上述查询计算,我们将获得下面结果:
SNAME ------- Smith Adams
关系微积分基于 第一顺序逻辑(first order logic)。 关系微积分有两个变种:
(数)域关系微积分(Domain Relational Calculus) (DRC),其变量代表元组的元素(属性)。
记录关系微积分(Tuple Relational Calculus) (TRC),其变量代表元组。
我们只打算讨论记录关系微积分,因为它是大多数关系语言背后的数学基础。 有关 DRC(当然还有 TRC)的详细讨论,参阅 Date, 1994 或 Ullman, 1988。
在TRC 里使用的查询是这样的形式:
x(A) ∣ F(x),这里x 是一个元组变量, A 是属性的集合而 F 是一个公式。生成的关系包含所有满足 F(t) 的元组 t(A)。
如果我们想用 TRC 回答例子 使用关系演算的一个例子 里的问题,我们可以写出下面查询:
{x(SNAME) ∣ x ∈ SUPPLIER ∧ ∃ y ∈ SELLS ∃ z ∈ PART (y(SNO)=x(SNO) ∧ z(PNO)=y(PNO) ∧ z(PNAME)='Screw')}
对 供应商和部件数据库 计算这个查询同样得出与 使用关系演算的一个例子 相同的结果。
关系演算和关系微积分有着相同的 表达能力;也就是说, 所有可以用关系演算表达的查询都可以使用关系微积分来表达, 反之亦然。这个结论首先是 E. F. Codd 在 1972 年证明的。 这个证明是基于一个算法(“Codd 的归纳算法”),利用这个算法, 一个任意的关系微积分的表达式可以归纳为一个语义上相等的关系演算的表达式。 关于这些更详细的讨论,请参考 Date, 1994 和 Ullman, 1988。
有时候我们说基于关系微积分的语言是比基于关系演算的语言 更 "高级的" 或者 "更描述性的" 语言, 因为演算(部分上)说 明了操作的过程而微积分把这些过程 留给编译器或者解释器以决定计算的最有效顺序。