关系代数 (数据库)

这里的关系代数不同于奥古斯都·德·摩根在1860年为代数逻辑提供的关系代数 关系代数是一阶逻辑的分支，是闭合于运算下的关系的集合。运算作用于一个或多个关系上来生成一个关系。关系代数是计算机科学的一部分。 在纯数学中的关系代数是有关于数理逻辑和集合论的代数结构。 关系代数在1970年E.F. Codd发表数据的关系模型之前很少受到注意。Codd曾是皮尔士选集编辑者Arthur W. Burks的博士研究生。Codd提议这样一种代数作为数据库查询语言的基础。第一个基于Codd的代数的查询语言是ISBL，許多作者都認同這個先驱的工作展示了一個使Codd的想法成为有用语言的方式。商务系统12是追随ISBL先例的短命工业级实力的关系DBMS。在1998年Chris Date和Hugh Darwen提议了一种叫Tutorial D的语言，意图用于教学关系数据库理论，它的查询语言也吸取了ISBL的想法。Rel是Tutorial D的一个实现。即使SQL的查询语言也松散的基于了关系代数，尽管SQL中的操作数（表）不完全是关系，很多有用的关于关系代数的理论在SQL对应者中不成立。 因为关系被解释为某个谓词的外延，关系代数的每个运算在谓词演算中都有对应者。例如，自然连接是逻辑AND（ ∧ {\displaystyle \land } ）的对应者。如果关系R和S分别表示谓词p1和p2的外延，则R和S的自然连接（R ⋈ {\displaystyle \bowtie } S）是表示谓词p1 ∧ {\displaystyle \land } p2的外延的关系。 认识到Codd的代数事实上关于一阶逻辑不完备是很重要的。实现它会引起不可逾越的特定计算困难。为了克服这些困难，他限制操作数为有限关系，并提议了对否定（NOT）和析取（OR）的有限支持。类似的限制在很多其他基于逻辑的计算机语言中也能见到。Codd定义术语关系完备性来称呼一个语言除了他提议的限制之外关于一阶逻辑是完备的。在实践中这些限制对他的关系代数用于数据库用途的适用性没有不利作用。 如同任何代数，一些运算是原始的，而可以通过原始运算来定义的另一些运算是导出的。尽管逻辑中的AND, OR和NOT的选取，某种程度上是任意性的是众所周知的，Codd对他的代数作了类似的任意选取。 Codd的代数的六个原始运算是“选择”、“投影”、笛卡尔积（也叫做“叉积”或“交叉连接”）、并集、差集和“重命名”。（实际上，Codd忽略了重命名，而ISBL的发明者显式地包括了它）。这六个运算在省略其中任何一个都要损失表达能力的意义上是基本的。已经依据这六个原始运算定义了很多其他运算。其中最重要的是交集、除法和自然连接。事实上ISBL显著的用自然连接替代了笛卡尔积，它是笛卡尔积的退化情况。 总之，关系代数的运算有与域关系演算或元组关系演算同样的表达能力。但是出于前面介绍中给出的原因，关系代数有严格弱于没有函数符号的一阶谓词演算的表达能力。关系代数实际上对应于一阶逻辑的子集，即没有递归和否定的Horn子句。 尽管六个基本运算中有三个取自集合论，在它们的关系代数对应者中存在额外的约束：对于并集和差集，涉及到的两个关系必须是“并集相容”的—就是说，两个关系必须有同样的属性集合。因为交集可以用差集来定义，交集所涉及的两个关系也必须是并集相容的。 笛卡尔积定义得与集合论有所不同，这里的元组是平坦的、无子元组的。就是说，不同于集合论，那里的n元组和m元组的笛卡尔积是2元组，而关系代数中它们的笛卡尔积把这个2元组展平为n+m元组。更形式的说，R × S被定义为： R × {\displaystyle \times } S = {r ∪ {\displaystyle \cup } s| r ∈ {\displaystyle \in } R, s ∈ {\displaystyle \in } S} 此外，对于要定义的笛卡尔积，涉及的两个关系必须有不相交表头—就是说，它们一定不能有公共属性名字。 投影是写为 π a 1 , . . . , a n ( R ) {\displaystyle \pi _{a_{1},...,a_{n}}(R)} 的一元运算，这里的 a 1 , . . . , a n {\displaystyle a_{1},...,a_{n}} 是属性名字的集合。这种投影的结果定义为当所有在 R {\displaystyle R} 中的元组被限制为集合 { a 1 , . . . , a n } {\displaystyle \{a_{1},...,a_{n}\}} 的时候所获得的集合。 广义选择是写为 σ φ ( R ) {\displaystyle \sigma _{\varphi }(R)} 的一元运算，这里的 φ {\displaystyle \varphi } 是由正常选择中所允许的原子和逻辑算子 ∧ {\displaystyle \land } （与）、 ∨ {\displaystyle \lor } (或)和 ¬ {\displaystyle \lnot } （非）构成的命题公式。这种选择选出 R {\displaystyle R} 中使 φ {\displaystyle \varphi } 成立的所有元组。 重命名是写为 ρ a / b ( R ) {\displaystyle \rho _{a/b}(R)} 的一元运算，这里的结果同一于 R {\displaystyle R} ，除了在所有元组中的 b {\displaystyle b} 字段被重命名为 a {\displaystyle a} 字段之外。它被简单的用来重命名关系的属性或关系自身。 自然连接是写为 (R ⋈ S)的二元运算，这里的R和S是关系。自然连接的结果是在R和S中的在它们的公共属性名字上相等的所有元组的组合。例如下面是表格“雇员”和“部门”和它们的自然连接： 连接是关系复合的另一种术语；在范畴论中连接精确的是纤维积。 自然连接被确证为最重要的算法之一，因为它是逻辑AND的关系对应者。仔细注意如果同一个变量在用AND连结的两个谓词中出现，则这个变量表示相同的事物而两个出现必须总是由同一个值来代换。特别是，自然连接允许组合有外键关联的关系。例如，在上述例子中，外键成立于从雇员.DeptName到部门.DeptName，雇员和部门的自然连接组合了所有雇员和它们的部门。注意这能工作因为外键在相同名字的属性之间保持。如果不是这样，外键成立于从部门.manager到Emp.emp-number，则我们必须在采用自然连接之前必须重命名这些列。这种自然连接有时叫做相等连接（参见θ-连接）。 更形式的说，自然连接的语义定义为： R ⋈ {\displaystyle \bowtie } S = { t ∪ {\displaystyle \cup } s : t ∈ {\displaystyle \in } R, s ∈ {\displaystyle \in } S, fun (t ∪ {\displaystyle \cup } s) } 这里的fun(r)是对于二元关系r为真的谓词，当且仅当r是函数二元关系。通常要求R和S必须至少有一个公共属性，但是如果省略了这个约束则在那种特殊情况下自然连接就完全变成上面定义的笛卡尔积。 自然连接可以用Codd的原始运算模拟如下。假定b1,...,bm是公共于R和S的公共属性名字，a1,...,an是唯一于R的属性名字而c1,...,ck是唯一于S的属性名字。进一步假定属性名字 d1,...,dm不在R 和S二者中。第一步我们可以重命名S中的公共属性名字：: S' := ρd1/b1(...ρdm/bm( S)...)，接着我们采用笛卡尔积并选择要连接的元组：: T := σb1=d1(...σbm=dm(R × S')...) ，最后我们采用一个投影来去掉重命名的属性：: U := πa1,...,an,b1,...,bm,c1,...,ck(T) 。 考虑分别列出车模和船模的价格的表“车”和“船”。假设一个顾客要购买一个车模和一个船模，但不想为船花费比车更多的钱。在关系上的θ-连接CarPrice ≥ BoatPrice生成所有可能选项的一个表。 如果我们要组合来自两个关系的元组，而组合条件不是简单的共享属性上的相等，则有一种更一般形式的连接算子才方便，这就是θ-连接（或theta-连接）。θ-连接是写为 R   ⋈   S a   θ   b {\displaystyle {\begin{matrix}R\ \bowtie \ S\\a\ \theta \ b\end{matrix}}} 或 R   ⋈   S a   θ   v {\displaystyle {\begin{matrix}R\ \bowtie \ S\\a\ \theta \ v\end{matrix}}} 的二元算子，这里的a和b是属性名字，θ是在集合{<, ≤, =, >, ≥}中的二元关系，v是值常量，而R和S是关系。这个运算的结果由在R和S中满足关系θ的元素的所有组合构成。只有S和R的表头是不相交的，即不包含公共属性的情况下，θ-连接的结果才是有定义的。 这个运算可以用基本运算模拟如下： R ⋈ {\displaystyle \bowtie } θ S = σθ(R × S) 在算子θ是等号算子 (=)的时候这个连接也相等连接。 但是要注意，支持自然连接和重命名的计算机语言可以不需要θ-连接，因为它可以通过对自然连接（在没有公共属性的时候的它退化为笛卡尔积）的选择来完成。 半连接是类似于自然连接的写为R ⋉ S的连接，这里的R和S是关系。半连接的结果只是在S中有在公共属性名字上相等的元组所有的R中的元组。例如下面的例子是“雇员”和“部门”和它们的半连接的表： 更形式的说半连接的语义定义如下： R ⋉ {\displaystyle \ltimes } S = { t : t ∈ {\displaystyle \in } R, s ∈ {\displaystyle \in } S, fun (t ∪ {\displaystyle \cup } s) } 这里的fun(r)定义同于自然连接。 半连接可以被使用自然连接模拟如下。假定a1,...,an是R的属性名字，则： R ⋉ {\displaystyle \ltimes } S = Π {\displaystyle \Pi } a1,..,an（R ⋈ {\displaystyle \bowtie } S） 因为我们可以通过基本运算模拟自然连接因此也就可以模拟半连接。 反连接是类似于自然连接的写为R ▷ S的连接，这里的R和S是关系。反连接的结果是在S中没有在公共属性名字上相等的元组的R中的那些元组。 例如“雇员”和“部门”和它们的反连接的表： 反连接形式定义为： R ▹ {\displaystyle \triangleright } S = { t : t ∈ {\displaystyle \in } R ∧ {\displaystyle \land } ¬ ∃ {\displaystyle \neg \exists } s ∈ {\displaystyle \in } S : fun (t ∪ {\displaystyle \cup } s) } 或 R ▹ {\displaystyle \triangleright } S = { t : t ∈ {\displaystyle \in } R，S中没有s满足fun (t ∪ {\displaystyle \cup } s) } 这里的fun(r)定义同于自然连接。 反连接还可以定义为半连接的补集： R ▹ {\displaystyle \triangleright } S = R - R ⋉ {\displaystyle \ltimes } S 为此反连接有时叫做反半连接，反连接算子有时写为其上有横杠的半连接符号。 除法是写为R ÷ S的二元关系。其结果由R中元组到唯一于R的属性名字（就是说只在R表头中而不在S表头中的属性）的限制构成，并且它们与S中的元组的所有组合都存在于R中。例如下面的“完成”和“DB项目”和它们的除法： 如果“DB项目”包含数据库项目的所有任务，则这个除法的结果精确的包含已经完成了数据库项目的所有学生。 更形式的说除法的语义定义如下： R ÷ S = { t[a1,...,an] : t ∈ {\displaystyle \in } R ∧ {\displaystyle \wedge } ∀ {\displaystyle \forall } s ∈ {\displaystyle \in } S ( (t[a1,...,an] ∪ {\displaystyle \cup } s) ∈ {\displaystyle \in } R) } 这里的{a1,...,an}是唯一于R的属性名字的集合而t[a1,...,an]是t到这个集合的限制。通常要求在S的表头中的属性名字是R的表头的属性名字的子集，否则运算的结果永远为空。 除法可以用基本运算模拟如下。我们假定a1,...,an是唯一于R的属性名字而 b1,...,bm是S的属性名字。在第一步中我们投影R于它的唯一属性上，并接着构造它们与S的元组的所有组合： T := πa1,...,an(R) × S 在上面例子中，T将是表示所有学生（因为Student是“完成”表的唯一键/属性）与所有给定任务的组合的表。所以Eugene在T中将有两行Eugene -> Database1和Eugene -> Database2。 在下个步骤中，我们从这个关系中减去R: U := T - R 注意在U的都是R中没有出现的可能的组合。所以如果现在做到唯一于R 的属性名字的投影，则我们有了R中元组的限制，它们与S的元组的所有组合都未出现在R中： V := πa1,...,an(U) 剩下的就是投影R到唯一于它的属性名字并减去V: W := πa1,...,an(R) - V 尽管连接（或内连接）的结果是由组合两个操作数的匹配元组而形成的元组组成，外连接由这些元组加上通过向一个操作数的未匹配元组扩展上另一个操作数的每个属性的“填充”值而形成的元组组成。 本节定义的运算假定“空”值ω的存在性，我们不定义它并把它用做填充值。不应当假定它为数据库语言SQL所定义的NULL，也不应该假定ω为一个标号而非一个值，也不应该假定它介入了有争议的三值逻辑。 定义三个外连接：左外连接、右外连接和全外连接。（有时省略“外”字） 左外连接写成R ⟕ S，其中R与S为关系。左外连接的结果包含R中所有元组，对每个元组，若在S中有在公共属性名字上相等的元组，则正常连接，若在S中没有在公共属性名字上相等的元组，则依旧保留此元组，并将对应其他列设为NULL。 ( R ⋈ S ) ∪ ( ( R − π r 1 , r 2 , … , r n ( R ⋈ S ) ) × { ( ω , … ω ) } ) {\displaystyle (R\bowtie S)\cup ((R-\pi _{r_{1},r_{2},\dots ,r_{n}}(R\bowtie S))\times \{(\omega ,\dots \omega )\})} 右外连接写成R ⟖ S，其中R与S为关系。右外连接的结果包含S中所有元组，对每个元组，若在R中有在公共属性名字上相等的元组，则正常连接，若在R中没有在公共属性名字上相等的元组，则依旧保留此元组，并将对应其他列设为NULL。 ( R ⋈ S ) ∪ ( ( S − π s 1 , s 2 , … , s n ( R ⋈ S ) ) × { ( ω , … , ω ) } ) {\displaystyle (R\bowtie S)\cup ((S-\pi _{s_{1},s_{2},\dots ,s_{n}}(R\bowtie S))\times \{(\omega ,\dots ,\omega )\})} 全外连接写成R ⟗ S，其中R与S为关系。全外连接的结果包含R与S中所有元组，对每个元组，若在另一关系上中有在公共属性名字上相等的元组，则正常连接，若在另一关系上中没有在公共属性名字上相等的元组，则依旧保留此元组，并将对应其他列设为NULL。 R ⟗ S = (R ⟕ S) ∪ (R ⟖ S) 多数数据库包括五个聚集函数。这些运算是Sum、Count、Average、Maximum和Minimum。在关系代数中被写为Exp1,Exp2,Exp3...Gfunc1,func2,func3...（关系）。必须指定要用的函数，而表达式是可选的。假定有一个叫Account的表有两列，分别是Branch_Name和Balance，并希望找到有最高结余的分部的名字，我们可以写Branch_NameGMax(Balance)（Account）。要找到最高余额我们可以简单的写GMax(Balance)（Account）。 尽管关系代数对于大多数用途都足够强大了，有一些在关系上的简单而自然的运算不能用关系代数表达。二元关系的传递闭包就是其中之一。给定一个域D，设二元关系R是DxD的子集。R的传递闭包R+是包含R的满足如下条件的DxD的子集： ∀ {\displaystyle \forall } x ∀ {\displaystyle \forall } y ∃ {\displaystyle \exists } z((x,y) ∈ {\displaystyle \in } R ∧ {\displaystyle \wedge } (y,z) ∈ {\displaystyle \in } R ⇒ {\displaystyle \Rightarrow } (x,z) ∈ {\displaystyle \in } R) 可以证明没有关系代数表达式E(R)接受R作为变量参数而生成R+。证明基于如下事实，给定一个关系表达式E，对它声称E(R) = R+，这里的R是变量，我们总是可以找到R的一个实例r(和一个对应的域d)使得E(r) ≠ r+。 查询可以表示为一个树，这里的 内部节点是算子， 叶子是关系， 子树是子表达式 主要目标是把表达式树变换成等价的表达式树，使得在树中的子表达式生成的关系的平均大小比优化前更小。次要目标是在一个单一查询中，或在要同时求值多于一个查询的时候的所有这些查询中，尝试形成公共子表达式。在次要目标背后的原理是计算公共子表达式一次就够了，其结果可以用于包含这个子表达式的所有查询中。 我们提出可用于这种变换的一组规则。 关于选择运算的规则在查询优化中扮演了最重要的角色。选择是可非常有效的减少在它的操作数中的行数的运算，所以如果我们设法把在表达式树中选择向着叶子方向移动，（子表达式产生的）内部关系的大小将被缩小。 选择是幂等性的（多次应用同一个选择有同样效果），和交换性的（应用选择的次序在最终结果中没有影响）。 σ A ( R ) = σ A σ A ( R ) {\displaystyle \sigma _{A}(R)=\sigma _{A}\sigma _{A}(R)\,\!} σ A σ B ( R ) = σ B σ A ( R ) {\displaystyle \sigma _{A}\sigma _{B}(R)=\sigma _{B}\sigma _{A}(R)\,\!} 其条件是更简单条件的合取的选择等价于针对这些单独条件的一序列选择，而其条件是析取的选择等价于选择的并集。可以使用这些恒等式来合并多个选择为更少的需要求值的选择，或分解它们使得其成员选择可以被移动或单独优化。 σ A ∧ B ( R ) = σ A ( σ B ( R ) ) = σ B ( σ A ( R ) ) {\displaystyle \sigma _{A\land B}(R)=\sigma _{A}(\sigma _{B}(R))=\sigma _{B}(\sigma _{A}(R))} σ A ∨ B ( R ) = σ A ( R ) ∪ σ B ( R ) {\displaystyle \sigma _{A\lor B}(R)=\sigma _{A}(R)\cup \sigma _{B}(R)} 叉积对于计算是最耗费的算子。如果输入关系有 N {\displaystyle N} 和 M {\displaystyle M} 行，结果将包含 N M {\displaystyle NM} 行。所以在应用叉积算子之前尽最大可能减少两个操作数的大小是非常重要的。 这可以有效的完成，如果叉积跟随着选择算子，比如 σ A {\displaystyle \sigma _{A}} ( R {\displaystyle R} × P {\displaystyle P} )。这是最合适考虑连接定义的情况。如果叉积不跟随着选择运算，我们可以尝试使用其他规则从表达式树更高层压下一个选择。 在上节情况下我们使用对复杂条件的分解规则把条件 A {\displaystyle A} 分解为条件 B {\displaystyle B} 、 C {\displaystyle C} 和 D {\displaystyle D} ，使得 A {\displaystyle A} = B {\displaystyle B} ∧ {\displaystyle \wedge } C {\displaystyle C} ∧ {\displaystyle \wedge } D {\displaystyle D} ，而 B {\displaystyle B} 只包含来自 R {\displaystyle R} 的属性， C {\displaystyle C} 只包含来自 P {\displaystyle P} 的属性， D {\displaystyle D} 包含 A {\displaystyle A} 的包含来自 R {\displaystyle R} 和 P {\displaystyle P} 二者的属性的那些部分。注意 B {\displaystyle B} 、 C {\displaystyle C} 或 D {\displaystyle D} 可能为空，则下列规则成立： σ A ( R × P ) = σ B ∧ C ∧ D ( R × P ) = σ D ( σ B ( R ) × σ C ( P ) ) {\displaystyle \sigma _{A}(R\times P)=\sigma _{B\wedge C\wedge D}(R\times P)=\sigma _{D}(\sigma _{B}(R)\times \sigma _{C}(P))} 选择在差集、交集和并集算子上是分配性的。使用下列三个规则在表达式树中把压入下面的集合运算中。注意，在差集和交集算子的情况下有可能在变换之后只把选择算子应用于某一个操作数。在如下情况下这是有意义的，操作数之一很小，而计算选择的花费超过了使用更小关系作为操作数的利益。 σ A ( R ∖ P ) = σ A ( R ) ∖ σ A ( P ) = σ A ( R ) ∖ P {\displaystyle \sigma _{A}(R\setminus P)=\sigma _{A}(R)\setminus \sigma _{A}(P)=\sigma _{A}(R)\setminus P} σ A ( R ∪ P ) = σ A ( R ) ∪ σ A ( P ) {\displaystyle \sigma _{A}(R\cup P)=\sigma _{A}(R)\cup \sigma _{A}(P)} σ A ( R ∩ P ) = σ A ( R ) ∩ σ A ( P ) = σ A ( R ) ∩ P = R ∩ σ A ( P ) {\displaystyle \sigma _{A}(R\cap P)=\sigma _{A}(R)\cap \sigma _{A}(P)=\sigma _{A}(R)\cap P=R\cap \sigma _{A}(P)} 选择与投影是交换性的，当且仅当在选择条件中引用的字段是在投影中的字段的子集。在投影之前进行选择可能有用，如果操作数是叉积或连接。在其他情况下，如果选择条件计算相对破费，把选择从投影中移动出来可能减少要测试的元组的数目（因为投影可能产生更少的元组，因为它清除由于取消字段导致的重复元组）。 π a 1 , . . . , a n ( σ A ( R ) ) = σ A ( π a 1 , . . . , a n ( R ) ) {\displaystyle \pi _{a_{1},...,a_{n}}(\sigma _{A}(R))=\sigma _{A}(\pi _{a_{1},...,a_{n}}(R))} 这里的 A {\displaystyle A\,} 中的字段 ⊆ { a 1 , . . . , a n } {\displaystyle \subseteq \{a_{1},...,a_{n}\}} 投影是幂等性的，所以一系列（有效）投影等价于最外投影。 π a 1 , . . . , a n ( π b 1 , . . . , b m ( R ) ) = π a 1 , . . . , a n ( R ) {\displaystyle \pi _{a_{1},...,a_{n}}(\pi _{b_{1},...,b_{m}}(R))=\pi _{a_{1},...,a_{n}}(R)} 这里的 { a 1 , . . . , a n } ⊆ { b 1 , . . . , b m } {\displaystyle \{a_{1},...,a_{n}\}\subseteq \{b_{1},...,b_{m}\}} 投影在并集上是分配性的。 π a 1 , . . . , a n ( R ∪ P ) = π a 1 , . . . , a n ( R ) ∪ π a 1 , . . . , a n ( P ) {\displaystyle \pi _{a_{1},...,a_{n}}(R\cup P)=\pi _{a_{1},...,a_{n}}(R)\cup \pi _{a_{1},...,a_{n}}(P)} 对一个变量的连续的重命名可以缩减为一个单一的重命名。没有公共变量的重命名运算可以任意相互重新排序，这可以导致能缩减的重命名毗连起来。 ρ a / b ( ρ b / c ( R ) ) = ρ a / c ( R ) {\displaystyle \rho _{a/b}(\rho _{b/c}(R))=\rho _{a/c}(R)\,\!} ρ a / b ( ρ c / d ( R ) ) = ρ c / d ( ρ a / b ( R ) ) {\displaystyle \rho _{a/b}(\rho _{c/d}(R))=\rho _{c/d}(\rho _{a/b}(R))\,\!} 重命名关于差集、并集和交集是分配性的。 ρ a / b ( R ∖ P ) = ρ a / b ( R ) ∖ ρ a / b ( P ) {\displaystyle \rho _{a/b}(R\setminus P)=\rho _{a/b}(R)\setminus \rho _{a/b}(P)} ρ a / b ( R ∪ P ) = ρ a / b ( R ) ∪ ρ a / b ( P ) {\displaystyle \rho _{a/b}(R\cup P)=\rho _{a/b}(R)\cup \rho _{a/b}(P)} ρ a / b ( R ∩ P ) = ρ a / b ( R ) ∩ ρ a / b ( P ) {\displaystyle \rho _{a/b}(R\cap P)=\rho _{a/b}(R)\cap \rho _{a/b}(P)} 在Unicode中，自然连接符号是⋈（U+22C8），在Latex中用\bowtie表示。 在Unicode中，半连接符号是⋉ (U+22C9)和⋊（U+22CA），前者在Latex中用\ltimes表示。 在Unicode中，反连接符号是▷（U+25B7），在Latex中用\triangleright表示。 在Unicode中，左外连接符号是⟕ (U+27D5) 在Unicode中，右外连接符号是⟖ (U+27D6) 在Unicode中，全外连接符号是⟗ (U+27D7) Aho, Alfred V.; Jeffrey D. Ullman. Universality of data retrieval languages. Proceedings of the 6th ACM SIGACT-SIGPLAN symposium on Principles of programming languages. 1979: 110–119.  引文使用过时参数coauthors (帮助) LEAP - An implementation of the relational algebra （页面存档备份，存于互联网档案馆） Query Optimization This paper is an excellent introduction into the use of the relational algebra in optimizing queries, and includes numerous citations for more in-depth study. 数据库 关系模型