张彤
张彤
Published on 2026-01-14 / 11 Visits
0
0

蓝象十日谈·第三日_3.1数据库系统的关系代数

数据库系统的关系代数

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

集合论

集合的概念和描述

集合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,人是其中的元素。则任何人都是自己的家人。

  2. 传递性(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。传递性是一种普遍的逻辑模式,它描述了关系连接的链式效应,从集合关系到语言用法,都强调了这种前推后导的特性。

    同样例子,你和爸爸是家人,你和妈妈是家人,则依据传递性,爸爸和妈妈是家人。

  3. 对称性(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是逆关系

  4. 反对称性(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

  2. 反对称性(Antisymmetric)

    \forall a,b\in A,\ (a\preceq b \land b\preceq a)\Rightarrow a=b

  3. 传递性(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 bb\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 里所有元素都小。若存在,则唯一

  2. 最大元(maximum / greatest element)

    M\in S 最大元,若:

    \forall x\in S,\ x\preceq M


    若存在,也唯一

  3. 极小元(minimal element)

    m\in S极小元,若:

    \neg \exists x\in S,\ x\prec m


    也就是 S 中找不到比它更小的元素。因为不可比会造成并列最小,极小元可能有多个

  4. 极大元(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_1E 中出现两次,但投影后只保留一次。

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

\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)(除非你写括号)。

  2. 连接与笛卡尔积是次高

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

    • 笛卡尔积: \times

    • \theta-连接: \Join_\theta

    • 自然连接: \Join

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

    比如

    R \Join S \times T \quad\text{默认按从左到右结合:}\quad (R \Join S)\times T

  3. 交运算 Intersection是更低一层的算子。

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

    R \cup S \cap T \quad\text{默认读作}\quad R \cup (S\cap T)

  4. 并运算和差运算 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) 的元组 tt是一个元组变量(可以访问 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_jx_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)。

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


Comment