跳转至

数据库系统的关系代数

关系代数的全部对象与运算,本质上都可以理解为:对元组集合做集合运算。因此理解关系代数前需要先理解集合论。

集合论

集合的概念和描述

集合set是一些对象的汇集,可以表示如下:

\[ A = \{a,b,c\} \]

某个元素是否属于集合,用 \(\in\)\(\notin\)( in / notin)表示:

\[ a \in A,d \notin A \]

集合有两个核心特征,无序性和不重复性

\[ \{a,b\} = \{b,a\}\\ \{a,a,b\} = \{a,b\} \]

这两点非常关键:在经典关系模型里,一张关系表就是一个集合,因此它的行不因顺序不同而不同,也不允许重复行.

描述集合的方法通常有枚举法\(A=\{1,2,3\}\),即列出所有元素。还有一类是描述法,用性质定义集合,比如 \(A=\{x∣x 是 1 到 10 的偶数\}\)。描述法会被频繁使用,因为关系代数中很多结果关系都用满足某种谓词的元组集合来定义。

子集

子集的定义

\[ A\subseteq B \iff \forall x(x\in A \Rightarrow x\in B) \]

直白的描述就是,如果A的元素都可以在B中找到,那么A就是B的一个子集。

真子集的定义

\[ A\subset B \iff A\subseteq B \ \text{且}\ A\neq B \]

和子集不同,真子集不包含A和B元素相同的情况。

集合相等

\[ A=B \iff A\subseteq B \ \text{且}\ B\subseteq A \]

这是后面证明关系代数等价变换的主要方法:要证明两个表达式等价,往往就是证明它们互为子集。

空集及全集

空集和全集是边界条件的来源。空集定义如下:

\[ \varnothing = \{\} \]

\(\varnothing\subseteq A\)任意集合 A 都成立,即使A是空集,这个定理也是成立的。很多对所有的命题,在空集上会表现出重要的边界语义,后面讲关系除法会用到。

全集不是固定的,它依赖讨论范围:

\[ U=\text{我们当前允许的所有对象} \]

引入全集后可以定义补集。

集合运算四件套

集合论中的并、交、差、补四件套是关系代数里并/交/差以及很多推导变换的数学基础。

并集(Union)

\[ A\cup B={x\mid x\in A\ \text{或}\ x\in B} \]

即两个集合的合并,元素要么在A集合,要么在B集合。

交集(Intersection)

\[ A\cap B={x\mid x\in A\ \text{且}\ x\in B} \]

集合A和和集合B两者共有的部分。

差集(Difference)

\[ A-B={x\mid x\in A\ \text{且}\ x\notin B} \]

从 集合A 中删掉 集合B 的元素。注意,差集是有方向和先后顺序的,一般情况下 \(A-B \neq B-A\)

补集(Complement)

给定全集 (U),则

\[ A^c = U-A \]

不在 集合A 里,但是在全集U内的元素。

关系、性质与等价性

关系代数中的关系有两层含义:

  1. 集合论的关系:集合 A 上的一个二元关系,是 \(A\times A\)的子集。

  2. 数据库的关系:是若干域的笛卡尔积上的子集,是更一般的 n 元关系

我们本节论述的是第一个含义,它们对后续理解等价变换、分组归并等非常关键。

二元关系的严格定义

给定集合 A,B,一个从 A到 B的二元关系 R 是笛卡尔积的子集:

\[ R \subseteq A\times B \]

若 A=B,则称 R 是 集合 A 上的二元关系

\[ R \subseteq A\times A \]

可记号约定为

\[ (a,b)\in R \\ aRb \]
  • 读作a 与 b 满足关系 R

关系的三大基本性质

假设 \(R\subseteq A\times A\)

  1. 自反性(Reflexive)
\[ R_\text{ 自反 } \iff \forall a\in A,\ (a,a)\in R \]
每个元素都与它自己相关。自反性强调关系指向自身,是描述事物恒等、自我包含或自我反思的属性。

比如,把R取为家人`family-of`,人是其中的元素。则任何人都是自己的家人。
  1. 传递性(Transitive)
\[ R_\text{ 传递 } \iff \forall a,b,c\in A,\ (a,b)\in R \land (b,c)\in R \Rightarrow (a,c)\in R \]
如果 a 通过 b 能到 c,那么 a 也能直接到 c。传递性是一种普遍的逻辑模式,它描述了关系连接的**链式效应**,从集合关系到语言用法,都强调了这种`前推后导`的特性。

同样例子,你和爸爸是家人,你和妈妈是家人,则依据传递性,爸爸和妈妈是家人。
  1. 对称性(Symmetric)
\[ R_\text{ 对称 } \iff \forall a,b\in A,\ (a,b)\in R \Rightarrow (b,a)\in R \]
对称性是双向奔赴的。就好比你对于妻子是家人,妻子对于你也是家人。很明显 \(a \neq b\)。
\[ R_{对称} \iff R^{-1} = R \]
- 其中 \(R^{-1}\) 和 \(R\)是逆关系
  1. 反对称性(Antisymmetric)

    除了上述三大性质,还有一类非常重要的性质,就是反对称性。

\[ R_\text{ 反对称 } \iff \forall a,b\in A,\ (a,b)\in R \land (b,a)\in R \Rightarrow a=b \]
如果 a 和 b 彼此都相关,即双向都成立,那它俩就不能是两个不同的元素;只能是同一个元素。**不允许出现两个不同元素之间的双向关系**。

把元素取为集合,关系取为包含于,则
\[ A \subseteq B \ and \ B \subseteq A \\ \therefore A = B \]
举例就是把R取为家谱`ancestor-of`,其中的元素是目录`item`,就是每一代的目录。若目录A是B的祖先,且B又是A的祖先,那么A和B只能是同一目录。层级关系中不允许相互在上层。

需要注意的是,`对称性`和`反对称性`在逻辑上并不是对立的,是可以同时出现的。
\[ \forall{a,b} \in A,aRb \Rightarrow bRa(对称性,symmetric) \\ \forall{a,b} \in A,(aRb \land bRa) \Rightarrow a= b(反对称,antisymmetric) \]
对称性关注有一条边,就必须有反向的边。反对称性强调如果出现双向边则只能是同一个点。

比如A的学号和A相同,反过来A的学号也和A相同,这是对称性的体现,关系是学生和学号的关系。如果A的学号和B的学号相同,且B的学号和A的也相同,那么A,B必然是一个人,这体现了反对称性。同时存在反对称和对称的前提就是关系中只能有一个元素。

> **反对称 vs 非对称(asymmetric)**
>
> 非常容易混肴的概念
>
> **非对称(asymmetric)**定义是更强的:
>
>
\[ \forall a,b,\ aRb \Rightarrow \neg(bRa) \]
>
>   
>
> 它直接禁止任何双向(包括 a=b 的情况),因此非对称关系必然**反自反**(不可能有 aRa)。
>
> 关系强弱包含关系是:
>
>
\[ \text{非对称} \Rightarrow \text{反对称} \]
>
>   
>
> 但反过来不成立:反对称允许 aRa,而非对称不允许。

等价关系

集合 A 上的关系 \(\sim\) 若同时满足:

  1. 自反性

  2. 对称性

  3. 传递性

则称 \(\sim\)等价关系equivalence relation)。

\[ \sim \text{ 是等价关系 } \iff \begin{cases} \forall a\in A,\ a\sim a & (\text{自反})\ \forall a,b\in A,\ a\sim b \Rightarrow b\sim a & (\text{对称})\ \forall a,b,c\in A,\ a\sim b \land b\sim c \Rightarrow a\sim c & (\text{传递}) \end{cases} \]

给定等价关系 \(\sim\),元素 \(a\in A\)等价类定义为:

\[ [a]={x\in A\mid x\sim a} \]

等价类的三个核心定理

  1. 定理1: \((a\in [a])\)

    因为自反 \(a\sim a\)

  2. 定理2: 若 \(a\sim b\),则 \([a]=[b]\)

    用传递性与对称性证明互为子集

  3. 定理 3:任意两个等价类要么相等,要么不相交

\[ [a]\cap [b]\neq \varnothing \Rightarrow [a]=[b] \]
因此等价类把集合 A 划分成互不相交的块。

所有等价类的集合称为商集:

\[ A/{\sim}={[a]\mid a\in A} \]

并且 {[a]} 构成对 A的一个划分:并起来等于整个集合,两两不相交,每个块非空。

这部分和数据库中按某个键分组的直觉非常接近:等价关系把对象归并成类。

偏序

给定集合 A,关系 \(\preceq\)偏序(partial order) 当且仅当它满足:

  1. 自反性(Reflexive)
\[ \forall a\in A,\ a\preceq a \]
  1. 反对称性(Antisymmetric)
\[ \forall a,b\in A,\ (a\preceq b \land b\preceq a)\Rightarrow a=b \]
  1. 传递性(Transitive)
\[ \forall a,b,c\in A,\ (a\preceq b \land b\preceq c)\Rightarrow a\preceq c \]

满足这三条的 \(A,\preceq\) 称为偏序集(poset)

\(a,b\in A\),若 \(a\preceq b\)\(b\preceq a\),称 a,b 可比。否则称 不可比incomparable)。

全序是在偏序条件的基础上再增加一条可比性:

\[ \forall a,b\in A,\ a\preceq b \ \text{或}\ b\preceq a \]

也就是任意两元素都可比,偏序不要求这一点,所以更宽松,因此叫

非严格偏序如上定义,允许 \(a\preceq a\)

\[ a\preceq b \iff (a\prec b \ \text{或}\ a=b) \]

严格偏序定义则如下:

\[ a \prec b \iff (a\preceq b \land a\neq b) \]

严格偏序满足:

  • 反自反\(\forall a,\ \neg(a\prec a)\)

  • 传递性\((a\prec b \land b\prec c)\Rightarrow a\prec c\)

偏序与等价的关系

等价关系:自反 + 对称 + 传递

等价关注归类和同一性,侧重对象分组。

偏序关系:自反 + 反对称 + 传递

关注层级和先后,侧重把对象排序。

两者关键区别在于

  • 等价关系允许不同元素互相相关(对称),表示同类

  • 偏序禁止不同元素互相都在关系中(反对称),以避免排序意义下的循环/混淆

偏序下的四类极值

\(A,\preceq\) 是偏序集, \(S\subseteq A\)

  1. 最小元 (minimum / least element)

    \(m\in S\)最小元,若:

\[ \forall x\in S,\ m\preceq x \]
它比 \(S\)里所有元素都小。若存在,则**唯一**。
  1. 最大元(maximum / greatest element)

    \(M\in S\)最大元,若:

\[ \forall x\in S,\ x\preceq M \]
若存在,也**唯一**。
  1. 极小元(minimal element)

    \(m\in S\)极小元,若:

\[ \neg \exists x\in S,\ x\prec m \]
也就是 \(S\) 中找不到比它更小的元素。因为不可比会造成并列最小,**极小元可能有多个**。
  1. 极大元(maximal element)

    M\in S 是 极大元,若:

\[ \neg \exists x\in S,\ M\prec x \]
同理,可能有多个。

“最小/最大”是全局比较(对所有)

“极小/极大”是局部不可再改进(不存在更小/更大)

关系代数

关系代数就是将一些本质为元组集合的关系 relation用一套算子变为另外一套关系。全程满足封闭性,输入是关系,输出也是关系。

关系的相关概念

每个属性 \(A_i\) 取值来自某个域 \(D_i\)

一个 n 元关系 R 的模式 schema 可写作`:

\[ R(A_1,\dots,A_n) \]

一个元组 \(t\)是对属性的赋值, \(t=(a_1,\dots,a_n)\),其中 \(a_i\in D_i\)。其中关系(relation)就是元组的集合:

\[ R \subseteq D_1\times\cdots\times D_n \]
  • 关系 = 笛卡尔积上的子集

经典关系代数用的是集合语义,包括元组不重复,没有顺序,投影天然去重。

SQL 的重复/NULL/外连接等,属于工程扩展,不在此文讨论。

关系代数的算子

为了方便介绍关系代数的各个算子,以学生和选课为统一举例。

设学生集合 \(S=\{s_1,s_2,s_3\}\),课程集合 \(C=\{c_1,c_2\}\)

选课关系(二维关系):

\[ E(S,C)={(s_1,c_1),(s_1,c_2),(s_2,c_1)} \]

必修课集合(一元关系):

\[ R(C)={(c_1),(c_2)} \]

选择 \(\sigma\)

选择算子就是按谓词筛元组,即筛选行。其定义为对关系 \(R\),谓词 \(p\)(关于元组属性的条件):

\[ \sigma_p(R)={t\in R\mid p(t)} \]

例如对选课关系进行选择:

\[ \sigma_{C=c_1}(E)={(s_1,c_1),(s_2,c_1)} \]

常用代数律,比如合并选择:

\[ \sigma_p(\sigma_q(R))=\sigma_{p\land q}(R) \]

投影 \(\pi\)

投影算子就是丢掉属性,即筛列。对属性集合 \(X\subseteq {A_1,\dots,A_n}\)

\[ \pi_X(R)={t|_X \mid t\in R} \]

其中 \(t|_X\) 表示把元组 \(t\)限制到属性集合 \(X\) 上。

选课关系投影到学生:

\[ \pi_S(E)={(s_1),(s_2)} \]
  • 投影除了筛选列外,为了集合语义,还需要去重。 \(s_1\)\(E\) 中出现两次,但投影后只保留一次。

常用代数律是幂等(收缩):

\[ \pi_X(\pi_Y(R))=\pi_X(R)\quad (X\subseteq Y) \]

并 / 交 / 差:集合运算

并兼容 Union-compatibility

\(\cup,\cap,-\)要求两个关系属性个数相同,且对应属性域相同,或者说是可比。

运算定义如下:

\[ R\cup S={t\mid t\in R\ \text{或}\ t\in S} \\ R\cap S={t\mid t\in R\ \text{且}\ t\in S} \\ R-S={t\mid t\in R\ \text{且}\ t\notin S} \]

交集可由差算子导出,是常用的派生算子:

\[ R\cap S=R-(R-S) \]

笛卡尔积 \(\times\)

\(R\subseteq D_1\times\cdots\times D_m,S\subseteq E_1\times\cdots\times E_n\),则:

\[ R\times S={(r,s)\mid r\in R,\ s\in S} \]

结果是一个 \(m+n\) 关系。

\(|R|=m, |S|=n\),则 \(|R\times S|=mn\)。这是为什么“连接”不能简单靠 × 硬算:会爆炸。

连接 \(\Join\)

连接是最核心的派生算子,是受约束的组合。其本质是,连接 = 先做 \(\times\) 生成候选,再用 \(\sigma\) 过滤匹配。

\(\theta\)-连接

给定连接条件 \(\theta\)

\[ R\Join_\theta S ;\stackrel{def}{=}; \sigma_\theta(R\times S) \]

自然连接(Natural Join)

当 R 与 S 有同名属性(比如都含 B),自然连接要求这些同名属性相等,并把重复列合并。可形式化写作:

\[ R\Join S = \pi_{\text{去重后的属性}}\bigl(\sigma_{\text{同名属性相等}}(R\times S)\bigr) \]

左连接/右连接/全连接属于扩展关系代数(带空值/外连接语义),经典纯关系代数里通常不把它们当基本算子。这里不讨论。

除法 \(\div\)

除法回答的不是有没有,而是是不是对所有都成立

\(E(S,C)\)\(R(C)\),定义:

\[ E\div R = {(s)\mid \forall (c)\in R,\ (s,c)\in E} \]

选出了把 \(R\)里每门课都选了的学生。

\[ E={(s_1,c_1),(s_1,c_2),(s_2,c_1)},\quad R={(c_1),(c_2)} \]

只有 \(s_1\) 同时匹配 \(c_1,c_2\),因此:

\[ E\div R={(s_1)} \]

用基本算子展开除法

\(X={S},Y={C}\),则有经典等价式:

\[ E\div R = (\pi_X(E)) - (\pi_X\bigl((\pi_X(E)\times R)-E\bigr)) \]

拆解如下:

  • 候选学生: \(\pi_X(E)\)

左表里出现过的 \(X\) 值,才有资格被考虑(比如出现过的学生)。

  • 候选学生 × 必修课得到所有应当出现的配对

\(\pi_X(E)\times R\)

把每个候选 \(x\) 与右边每个 \(y\)配对,得到理论上必须存在的配对集合

  • 缺失配对

\((\pi_X(E)\times R)-E\)

把实际存在的配对 \(E\) 减掉,剩下的就是缺的那几门

  • 不合格的 \(X\)(至少缺一门)

\(\pi_X\bigl((\pi_X(E)\times R)-E\bigr)\)

只要某个 \(x\) 出现在缺失配对里,就说明它缺课,不合格。

  • 候选减去缺课学生,即为全修完的学生

\(\pi_X(E)-(\text{不合格X})\)

除法的逻辑等价如下:

\[ x\in (E\div R) \iff \neg \exists y\in R,\ (x,y)\notin E \]

\(x\) 合格,当且仅当 不存在 一门必修课 ( \(y\)) 使得 ( \(x,y\)) 不在选课关系里。

这就是 \(\forall\) for all被改写成 \(\neg\exists\neg\)\neg\exists\neg 的经典套路:

\[ \forall \equiv \neg\exists\neg \]

关系代数中的笛卡尔积 \(\times\)和 除法 \(\div\) 并不是互逆的。

在代数中,我们定义运算 \(f(x)\)\(g(x)\)互逆,通常意味着:

\[ 在合适的定义域和值域上成立 \\ g(f(x)) = x \\ f(g(y)) = y \]

而在关系代数中:

\(\times\) 的输入是两个关系,输出是更宽、更大的关系;

\(\div\) 的输入是两个关系,但它的输出丢掉了右侧属性,是更窄的关系;

\(\times\)引入大量信息(所有组合), \(\div\) 的定义是基于对所有的约束抽取,过程里发生了信息不可逆的压缩

因此一般不可能要求:

\[ (R\times S)\div S = R\quad\text{且}\quad (R\div S)\times S = R \]

对所有关系都适用,例外如下

\(E\)恰好只包含 \(Y\in R\) 的配对,并且对所有 \(x\in(E\div R)\) 都是完整的 \(x\times R\),则可能得到:

\[ (E\div R)\times R = E \]

非常少见的情况,不是普遍的真理。

反方向则更不可能 \((E\times R)\div R = E\)

\(E\times R\)的属性会变得更宽(多出一份 \(Y\) 或其它属性),结构与 \(E\) 不同;

即便你设计得让属性可对齐, \(\times\) 生成的是所有组合,这会人为制造很多对所有都成立的候选,从而改变除法结果。

因此先乘再除回去等价于原关系,通常是不成立的


总结

  • \(\sigma\):在集合里挑满足条件的元组

  • \(\pi\):在元组上抹掉不关心的属性(并去重)

  • \(\cup,\cap,-\):对同构关系做集合运算

  • \(\times\):把两个关系做全组合(一般太大,常作为中间步骤)

  • \(\Join:\sigma(R\times S)\) 的工程化表达

  • \(\div\):把“对所有”变成可计算的集合差与投影,是在表达一个都不能少

各算子的优先级

括号 (...) 永远最高优先级,先算括号里的子表达式。如果没有括号,就按约定的优先级从高到低绑定。

按照这个规则,我们在书写关系代数的时候,宁可多用括号来梳理计算的优先级别,也不要让读者去猜。

除括号外,各算子的优先级从高到低如下:

  1. 一元算子优先级最高。因为它们只修饰一个关系

    • 选择: \(\sigma_p(\cdot)\)

    • 投影: \(\pi_X(\cdot)\)

    • 重命名: \(\rho(\cdot)\)

    它们会优先绑定到右边最近的关系符号上。比如:

\[ \sigma_pR \Join S \quad\text{默认读作}\quad (\sigma_p(R)) \Join S \]
而不是 \(\sigma_p(R\Join S)\)(除非你写括号)。
  1. 连接与笛卡尔积是次高

    以下这些运算位于同一运算优先级:

    • 笛卡尔积: \(\times\)

    • \(\theta\)-连接: \(\Join_\theta\)

    • 自然连接: \(\Join\)

    除法 \(\div\)是二元构造型算子,且语义上通常先把结构性组合做完再做集合并差。所以也在这一层。

    比如

\[ R \Join S \times T \quad\text{默认按从左到右结合:}\quad (R \Join S)\times T \]
  1. 交运算 Intersection是更低一层的算子。

    类似算术里乘除高于加减,交 \(\cap\)运算更紧实,所以优先级高于并和差。

\[ R \cup S \cap T \quad\text{默认读作}\quad R \cup (S\cap T) \]
  1. 并运算和差运算 Union / Difference优先级最低的算子。

    \(\cup,\quad -\)并与差通常放在同一最低层级,并按从左到右结合。

\[ R - S - T \quad\text{默认读作}\quad (R-S)-T \]

当你连续写同优先级的二元算子时,常见约定是左结合 left-associative

  • \(R \Join S \Join T := (R \Join S)\Join T\)

  • \(R \times S \times T := (R \times S)\times T\)

  • \(R \cup S \cup T := (R \cup S)\cup T\)

  • \(R - S - T := (R - S)-T\)

注意:结合性是怎么加括号的约定;并不意味着所有算子都满足严格的结合律。

例如差集就不满足交换律,结合后语义也会变


关系演算

关系代数像过程语言,你告诉系统怎么做,先筛、再连、再投影之类。关系演算则更像声明语言,你告诉系统你要什么,满足哪些逻辑条件的元组/值。前者是优化基础,算子闭包 + 等价变换;后者是语义基础,算子闭包 + 等价变换。两者是数据库理论的两条主线。

关系元组演算 TRC

Tuple Relational Calculus

设关系模式为 \(R(A_1,\dots,A_n)\)。变量表示整行元组(tuple),通常以以下形式呈现

\[ \{t∣P(t)\} \]

即所有满足谓词 \(P(t)\) 的元组 \(t\)\(t\)是一个元组变量(可以访问 \(t.A_i\))。 \(P(t)\) 是一阶逻辑公式(可包含 \(\land,\lor,\neg,\exists,\forall\)等)

原子公式 building blocks

TRC 的谓词里常用的原子形式包括:

关系成员\(R(t)\),表示元组 \(t\)属于关系 \(R\)。更常见写法是对 \(t\)的属性与关系模式匹配,例如: \(R(t.A_1,\dots,t.A_n)\)

属性比较\(t.A_i \theta u.B_j\),或 \(t.A_i \theta c\),其中 \(\theta\in\{=,\neq,<,\le,>,\ge\}\)\(c\) 是常量。

逻辑连接与量词\(\land,\lor,\neg,\exists,\forall\)

想要一个结果关系的元组 t,可以写:

\[ \{t∣R(t)∧t.A>10∧∃u(S(u)∧u.B=t.B)\} \]

关系域演算 DRC

Domain Relational Calculus

变量表示属性值domain value),不是整行。

通常写成:

\[ \{⟨x1,…,xk⟩∣Q(x1,…,xk)\} \]

所有满足公式 \(Q\)\(k-元组值\)组合。 \(x_i\)是值变量(对应某个域), \(Q\)中通过关系谓词把这些值落在关系里,例如 \(R(x,y,z)\)

DRC 的原子公式常见形式:

  • \(R(x_1,\dots,x_n)\):表示 \((x_1,\dots,x_n)\in R\)

  • 值比较: \(x_i\theta x_j\)\(x_i\theta c\)

  • 同样支持 \(\land,\lor,\neg,\exists,\forall\)

DRC例子:

\[ {⟨x⟩∣∃y(E(x,y)∧R(y))} \]

域独立

上面说的不是所谓的表里出现过的值,而是数学上的取值域,比如整数域 Z,字符串集合,所有可能的日期,上述这些都是无限的,变量不受限制,范围则可能是无限大的,这也是关系域中要特别强调的,安全和域独立。

假设 \(R(x)\) 是一元关系,表示数据库里记录的某些整数。比如:

\[ R=\{1,2,3\} \]

我们现在进行域演算

\[ {⟨x⟩∣¬R(x)} \]

所有不在 \(R\)里的 \(x\)

如果 \(x\)的取值范围是整个整数域 \(\mathbb{Z}\),那答案就是:

\[ Z∖{1,2,3}=\{…,−2,−1,0,4,5,6,…\} \]

这是无限集合。数据库没法把它输出成一个有限关系

比如你查“没选课的学生”,你期待结果只会是某些学生 ID,而不会出现一个数据库里根本不存在的随机 ID。

活跃域(active domain)

\[ adom(D)={数据库实例 D 中出现过的所有常量} \]

如果数据库里只出现过 \(\{1,2,3\}\),那

\[ adom(D)={1,2,3} \]

那么真正像要的结果是

\[ \{⟨x⟩∣x∈adom(D)∧¬R(x)\} \]

这样结果必然是有限的(因为 \(\text{adom}(D)\) 有限)。

保证域安全和域独立是数据库系统理论的基础,离开这个基础,将无法返回结果。

Codd 定理:关系代数与安全的关系演算(TRC/DRC)在表达能力上等价。换句话说:你用代数能表达的查询,用安全演算也能表达,反之亦然。

这个结论解释了为什么数据库可以让用户写“声明式”的 SQL,接近演算,而内部可以用代数树去优化与执行。

数学模型与工程实现

前面讲了那么多数学概念,实际上都是为了后面的工程实现做铺垫。总而言之就是关系代数是集合语义的数学模型SQL 是工程语言。工程中,我们往往直接从业务需求到编写SQL语句,而不会去考虑所谓的选择,映射,连接之类的算子。关系代数之所以重要并不是在工程实现上,而是在系统层面解决了SQL语言无法定义的事情:语义,等价,优化,以及可分析性。工程实现上的jion只是表层,数据库内部的所有优化与执行,都是在关系代数及其变体的框架内发生。

另外,在项目设计阶段,关系代数通常不会以 \(\sigma,\pi ,\Join\)这样的形式出现,但是对应的那套抽象在一些关键设计工作中有所体现。例如:

  • 输入哪些关系( 实体/事实/维表

  • 做出哪些筛选(谓词)

  • 如何关联(连接键与基数)

  • 输出哪些属性(投影)

  • 是否去重(语义袋/集合)

这些都是关系代数的工程化抽象。

大型项目的痛处往往不是写SQL,而是指标口径的一致性。当多个团队,多个系统上产出指标时,为了口径一致,必须回答这个指标在那一步进行的过滤,不同节点上的过滤条件是否可以交换;这个去重是连接后还是连接前,结果是否等价;这个排除条件是差集还是反链接,边界条件是什么,这些都是需要用数学模型去约束的。最终目的是口径可复现,口径可审计,口径可对齐。

从模型到工程还是有些出入的,主要表现在以下几点:

  1. 本文前半部分默认集合语义;进入 SQL 后,若不加 DISTINCT,许多等价变换只在袋语义下部分成立或需要额外条件。

  2. 凡涉及 NULL 的比较,都要考虑 Unknown;因此 SQL 的筛选条件与连接条件在 NULL 上会偏离经典代数(关系代数的谓词是二值逻辑)。

  3. 左/右/全连接依赖补空值的语义,本质是扩展代数。而关系代数篇讲的是内连接为核心的闭包体系

  4. 关系代数保证语义等价;SQL 性能是否等价取决于优化器与物理执行。

  5. SQL 没有除法算子,但可以用两类范式表达同一语义:反证式(NOT EXISTS)与计数式(GROUP BY/HAVING)。

了解完数学模型和工程实现的联系与区别后,我们可以开始下面的数据库开发部分了。