资讯动态

关系代数入门:理解SQL背后的数学逻辑与查询优化原理

发布时间:2026/9/17 14:13:16 来源:尧图企业网站定制
入门关系代数理解SQL背后的数学逻辑其实没那么难如果你正在学数据库课程或者准备计算机相关的面试大概率会在某个角落碰到“关系代数Relational Algebra”这个词。很多教材把它放在SQL前面讲得又短又抽象让不少同学误以为这只是纯理论的数学符号游戏跟实际写SQL没什么关系。在进入今天的内容之前先把关系代数的位置说清楚关系代数不是SQL的替代品而是SQL查询的“底层语法”。你可以把关系代数理解成一台机器内部真正的运转逻辑SQL则是操作这台机器时使用的按钮和面板。你按按钮能实现某个功能但按钮背后是机器零件在按照固定的机械原理运动。关系代数就扮演了那套“机械原理”。今天这篇是数据库SQL系列的第七讲重点拆解关系代数到底在做什么、六个基本运算符和一组常用扩展运算符的用法、它跟SQL查询的映射关系、以及在实际工作中怎么利用关系代数思想排查慢查询和优化SQL。内容尽量用白话讲例子全部能直接跑适合正在复习数据库理论的在校生、准备面试跳槽的开发人员以及平时写SQL但一直没系统补过理论基础的从业者。1. 关系代数是什么先搞清楚它解决什么问题关系代数是一种面向集合的查询语言它操作的对象是“关系”也就是我们习惯叫的“表”。一次操作接收一个或两个关系作为输入经过运算后输出一个新的关系。因为输出还是关系所以多个操作可以嵌套组合形成复杂的查询表达式这种性质叫做“闭包性”。闭包性是关系代数最核心的设计思想之一。有了闭包性你才能把简单的操作像搭积木一样拼成复杂的查询。这也直接影响了SQL的设计——SQL的SELECT语句套SELECT语句本质上就是利用了这个特性。如果你理解了这个点看嵌套子查询的时候就不会觉得它是SQL特有的魔法它只是关系代数组合方式的另一种写法。1.1 一句话理解关系代数在数据库里的位置在完整的关系数据库理论体系里一共包含三块内容关系数据结构、关系操作集合、关系完整性约束。关系数据结构说的是“数据怎么组织”即表和表的形状关系完整性约束说的是“数据怎么保证合法”比如主键不能为空、外键必须存在而关系操作集合关心的就是“数据怎么被查询和更新”其中查询部分的核心就是关系代数。传统教材会把关系代数分类成两类运算一类是“集合运算”包括并、差、交、笛卡尔积另一类是“专门的关系运算”包括选择、投影、连接、除法。这种分法是为了教学方便但实际使用中我更建议大家按照“这个运算作用于一张表还是两张表”来分类因为这在写表达式时决定了你要先准备几个关系。1.2 为什么学了SQL还要学关系代数最直接的原因是SQL是一种实现语言关系代数是一种逻辑语言。SQL关心的是怎么写关系代数关心的是“到底计算了什么”。两者之间的差距就是优化器做的工作。数据库的查询优化器在执行SQL之前会先把SQL语句等价转换成关系代数表达式然后对表达式进行一系列等价变换比如把选择操作尽量下推、把连接顺序重新排列、消除冗余的投影等等。这个过程决定了你的SQL最终以什么顺序扫描表、以什么顺序做关联。换句话说你不会关系代数就永远只能“猜”优化器为什么这么走也就很难从根本上理解执行计划里的很多细节。另外还有一个非常实际的需求——面试。关系代数是数据库面试题的高频内容尤其是“用关系代数表示某个查询”“判断两个表达式是否等价”这两类题目。没有扎实的基础考场上是很难临时推导出来的。这一点我在后面的章节会专门展开讲。2. 六大基本运算符详解它们是关系代数的地基关系代数定义了六种基本运算选择σ、投影π、并∪、差−、笛卡尔积×、重命名ρ。这六种运算就像机器里的六个基础零件其他所有操作比如交、连接、除法都可以用这六种组合出来。也正因为这六种运算构成了一套“完备”的系统考试中经常会出现“用基本运算表示扩展运算”的题目。所以这六个运算符不是记个符号就行而是要真正理解它们的语义边界。2.1 选择σ按行过滤返回满足条件的元组选择运算记作σcond(R)意思是把关系R里满足条件cond的所有行取出来。它有两大特点第一选择运算是“水平分割”它只过滤行不改变表的列结构。第二选择条件里的比较运算符包括、≠、、、≥、≤还可以用∧与、∨或、¬非连接多个条件。举个例子有一张学生表student(sno, sname, age, dept)要找出计算机系年龄大于20岁的学生表达式就是σdeptCS ∧ age20(student)很多人初学时会混淆选择与SQL中的WHERE认为选择就是WHERE。这个理解没问题但不够完整。SQL中的WHERE除了能做选择在关联查询里还承担了一部分“连接条件”的职责而在关系代数里连接条件是被单独定义在连接运算中的。这个区别后面讲连接时再展开。2.2 投影π按列选择返回指定属性投影运算记作πattr1, attr2, ...(R)它把关系R中的指定列取出来并自动去重。“自动去重”是投影的一个重要行为。因为关系代数中一切对象都是集合集合里不允许出现重复元素所以投影后的结果如果出现重复行只会保留一行。这个去重行为看似简单实际上对性能有很大影响。SQL中的SELECT DISTINCT就是在执行类似投影后去重的工作代价很高这就是为什么我建议日常开发中不要轻易加DISTINCT除非业务上确实需要。举个例子查询所有学生所在的院系表达式是πdept(student)。如果计算机系有100个学生结果里CS只会出现一次。2.3 并∪、差−集合操作的基石并运算R∪S要求R和S具有相同的属性集合结果返回属于R或属于S的所有元组重复的只保留一份。差运算R−S同样要求R和S结构相同结果返回属于R但不属于S的元组。这两个运算在SQL中的对应分别是UNION和EXCEPT有的数据库写作MINUS。有一个细节特别容易踩坑并和差的相容性要求是两个关系的属性个数相同且对应属性的数据类型一致。在SQL中UNION会对列名和类型做严格校验所以你在写实际SQL时经常需要给列起别名目的就是让两个结果集在结构上“对齐”。并和差配合可以完成很多看起来复杂的查询。比如“查询既选修了课程1又选修了课程2的学生”可以写成πsno(σcno1(SC)) ∩ πsno(σcno2(SC))但交集不是基本运算所以用差来表示先算出选修了课程1的学生再减去“选修了课程1但没有选修课程2”的学生。这个思路在面试题里经常用到。2.4 笛卡尔积×最需要警惕的运算笛卡尔积R×S把R中的每一行与S中的每一行做组合结果关系包含R和S的所有列行数是两者行数的乘积。如果R有m行、S有n行结果就有m×n行。这个运算在语义上很好理解但在实际执行时是性能杀手。一个2万行的表和另一个3万行的表做笛卡尔积结果就是6亿行这个中间结果可能直接撑爆内存。SQL中如果忘记写连接条件或者连接条件写错就会在底层触发笛卡尔积这也是为什么DBA总是反复强调“多表查询一定要检查关联条件”。但从理论角度看笛卡尔积又是连接运算的基础。连接本质上就是先做笛卡尔积再按照某种条件做选择这个观点是理解一切连接运算的关键。2.5 重命名ρ解决“同名冲突”与“自连接”问题重命名运算记作ρS(A1,A2,...)(R)意思是将关系R重命名为S并把属性改名为A1、A2等。重命名看起来不起眼却是关系代数里非常关键的工具。它在两个场景下必不可少第一个场景是自连接。如果把一张表跟自己比较比如“查询年龄比“张三”大的学生”需要把student表跟自身做笛卡尔积或连接这时如果不重命名两个表的属性名完全相同根本无法区分“左边这张表的年龄”和“右边那张表的年龄”。SQL里用表的别名来解决这个问题关系代数里就是重命名运算。第二个场景是运算结果列名冲突。投影或连接后如果要继续运算必须保证属性名有唯一性这时也需要重命名。3. 连接家族全解等值连接、自然连接、theta连接、外连接连接运算是关系代数里用得最多、也最容易混淆的一组操作。它的核心思想是把两张表按照某种条件“拼”成一张宽表。所有连接运算都可以从笛卡尔积和选择的角度来理解。3.1 theta连接θ连接最通用的连接形式theta连接记作R⋈θS其中θ表示任意比较条件比如R.A S.B。它的计算方式非常简单先计算R×S然后对笛卡尔积结果执行选择σθ。从这个定义可以看出theta连接并没有增加新的计算能力它只是把“笛卡尔积选择”合并成一个操作让表达式更紧凑、语义更清晰。当θ取等号时theta连接就退化为等值连接R⋈ABS。需要特别注意的是等值连接的结果里会包含两个相同的列R.A和S.A各一份虽然它们的值相等但在结果关系中它们是不同的属性。3.2 自然连接去掉重复列的等值连接自然连接记作R⋈S它比等值连接多了两个行为第一自动比较两个关系中同名属性的值只有值相等时才拼接第二结果中同名属性只保留一份。这两个行为让自然连接用起来非常方便但也带来了隐蔽的风险。比如两张表都有一个名为“name”的属性但语义其实不同一个是学生姓名一个是院系名称自然连接会拿这两个name做等值匹配产生完全错误的结果。所以在我个人的实践中一般不推荐在关系代数表达式中随意用自然连接更不推荐在SQL中直接NATURAL JOIN。SQL标准确实支持NATURAL JOIN但一旦表结构变化它就会悄悄改变连接逻辑排错成本极高。用显式的ON条件写明连接依据才是稳妥的做法。3.3 半连接与反连接性能优化里的常客半连接记作R⋉S表示返回R中那些“能与S中至少一行匹配”的行。它的结果只包含R的列不会把S的列拼进来。反连接是半连接的补集返回R中“与S中任何一行都不匹配”的行。在SQL中半连接通常写成EXISTS子查询或IN子查询反连接通常写成NOT EXISTS或NOT IN。优化器在生成执行计划时经常会把相关的子查询改写成半连接或反连接因为这种形式可以直接利用特殊的执行算法如hash semi join避免冗余的输出列和重复计算。这也是为什么考试和面试里经常出现“用关系代数表示IN/NOT IN查询”的原因因为背后对应的正是半连接和反连接。3.4 外连接保留不匹配行的连接外连接分为左外连接⟕、右外连接⟖和全外连接⟗。左外连接在自然连接的基础上额外保留左边关系中所有无法匹配的行这些行在右表对应的列上置为NULL。理解外连接的一个好办法是把它拆解成三步先做自然连接得到匹配成功的行再找出左边关系中未匹配的行最后把这两部分结果并起来。如果在SQL中碰到复杂的LEFT JOIN你能用这个思路把查询拆开分析定位问题就快得多。外连接在关系代数里属于扩展运算可以用基本运算组合出来。但在平时的分析中我们不需要刻意这样拆直接使用外连接符号就能表达清楚。4. 除法运算解决“至少包含全部”类问题除法运算是关系代数中比较独特的一个操作也是很多人在学习和面试中的痛点。它解决的问题可以概括为“找出那些与给定集合中所有元素都有联系的元素”。4.1 除法的直观理解除法的标准记法是R÷S要求S的属性集合是R属性集合的子集。看一个经典例子选课关系SC(sno, cno)表示“学生选了哪些课程”课程关系C(cno)表示“课程表”。现在要查询“选修了全部课程的学生学号”用除法写就是SC÷C。这个查询直观理解是找出这样的学生他对课程表中的“每一门课”都有对应的选课记录。语言上描述“全部”“所有”“至少包含”这类全称量词的问题几乎都可以转换成除法这也是它在考题里出现频率极高的原因。4.2 用基本运算实现除法如果考试要求你用基本运算来表达除法你需要记住一个经典的转换公式R÷S可以用“差、笛卡尔积、投影”组合出来。假设R的属性集合为{A1,...,Am,B1,...,Bn}S的属性集合为{B1,...,Bn}除法的结果为R中那些“能与S做笛卡尔积后仍然全部落在R中”的A值。这个思路涉及多步运算我在当年准备面试时专门推演过一遍感兴趣的话可以自己画两张小表做验证。关键点是先用投影得到R中所有可能的A值再用A值与S做笛卡尔积产生“所有A组合所有B”的完整预期然后用差运算找出“预期中没出现在R里的组合”最后再差一次得到“包含全部S组合的A”。这个链条看着绕但每步都很标准理解了闭包性质之后你完全可以直接在草稿纸上推出来。4.3 SQL中为什么没有直接的除法关键字你可能会奇怪既然除法这么有用为什么SQL里没有直接的DIVIDE BY操作符原因是SQL的表达能力足够强可以用NOT EXISTS嵌套来实现同样的逻辑。但写出来往往比较绕容易出错。我见过的很多开发同学在写“查询选了所有课程的学生”这类需求时要么用COUNT比较要么用双层NOT EXISTS前者逻辑不严谨后者可读性差。如果你在面试中遇到这类题建议先画出关系代数表达式再一步步翻译成SQL。掌握除法之后这类“全称量词”问题的SQL写法会变得有章可循而不是每次靠背模板。5. 关系代数与SQL的映射边看边对照关系代数并不是和SQL并列的两套体系SQL的查询语句都可以用关系代数表达式来表达反过来大部分关系代数表达式也能直接改写成SQL。掌握这种对应关系能让你真正“看懂”SQL背后的执行逻辑。5.1 基础映射对照表关系代数SQL选择 σcond(R)SELECT * FROM R WHERE cond投影 πA,B(R)SELECT DISTINCT A, B FROM R并 R∪SSELECT ... FROM R UNION SELECT ... FROM S差 R−SSELECT ... FROM R EXCEPT SELECT ... FROM S笛卡尔积 R×SSELECT * FROM R CROSS JOIN S等值连接 R⋈ABSSELECT * FROM R JOIN S ON R.A S.B自然连接 R⋈SSELECT * FROM R NATURAL JOIN S左外连接 R⟕SSELECT * FROM R LEFT JOIN S ON ...半连接 R⋉SSELECT * FROM R WHERE EXISTS (SELECT 1 FROM S WHERE ...)反连接 R▷SSELECT * FROM R WHERE NOT EXISTS (SELECT 1 FROM S WHERE ...)这张表一定要自己亲手对照着写几遍因为它几乎覆盖了日常开发中90%以上的查询形态。其中有两个点需要提醒第一投影对应SQL中的SELECT DISTINCT而不是简单SELECT。因为关系代数的关系是集合不允许重复所以只要你写的是关系代数去重就是默认行为。但在SQL中普通SELECT默认不去重只有加了DISTINCT才跟投影语义完全一致。这也是关系代数与SQL一个非常本质的语义差异。第二UNION对应并运算没问题但UNION ALL在关系代数中没有直接对应因为UNION ALL允许重复行这违背了“关系是集合”的基本假设。不过在实际业务中UNION ALL往往执行效率更高因为省掉了去重代价。这是理论和工程之间的常见取舍。5.2 分组聚合在关系代数中有对应吗这是一个经常被问到的问题。答案很明确经典关系代数不包含分组聚合操作。关系代数处理的是集合层面的运算它没有“对每个分组做计算”这种操作的直接符号表示。但SQL中的GROUP BY和聚合函数COUNT、SUM、AVG、MAX、MIN是日常开发中不可或缺的。所以后来的关系代数扩展版本增加了分组聚合运算符记作γ。比如查询每个院系的学生人数可以写成γdept, COUNT(sno)→cnt(student)。不过传统教材和大纲里主要讲的是基础六运算加上扩展的连接和除法分组聚合一般不会作为重点展开面试中也很少让你写带γ的表达式。我觉得这个“缺失”其实很能说明一个问题关系代数是逻辑层面的工具它的目标是描述查询的含义而不是描述执行的效率。真正把分组聚合落实到执行计划里是SQL引擎和优化器的任务。5.3 从关系代数视角看SQL优化了解了关系代数表达式的等价变换你才算真正拿到了SQL优化的一把钥匙。优化器在生成执行计划前会对关系代数表达式树做一系列等价变换这些变换基于关系代数的定律其中最重要的几个是选择下推σcond(R⋈S)可以变换为σcond(R)⋈S当cond只涉及R的列提前过滤掉不相关的行减少上层运算的数据量。这条规则在SQL调优里的体现就是多表关联前先把单表条件写在WHERE里或者直接写在ON子句中而不是等关联完再过滤。投影消除冗余列提前去掉用不到的列减少中间结果的大小。连接顺序重排对多个表的连接顺序做优化尽量把小表作为驱动表减小中间结果规模。谓词化简与传递比如把 col A 且 col B 这种不可能为真的条件提前识别出来直接跳过执行。有一次我在排查一条慢查询时发现SQL里先对一张10万行的表做了子查询过滤再与另一张表关联执行计划走了全表扫描耗时3秒。后来我手动把过滤条件改写并前置让优化器能够把选择下推到表扫描阶段查询直接降到100毫秒以内。这个案例充分说明理解了优化器的等价变换规则你在写SQL时就能“顺着优化器的思路”写而不是跟它对抗。6. 常见问题与面试题速查含避坑经验关系代数这部分很多人在自学时会遇到各种卡点。我把这些年见过的高频问题和面试题整理成一份速查希望能帮你快速定位自己的薄弱环节。6.1 容易踩的坑第一混淆选择与投影的作用方向。选择是水平过滤行投影是垂直过滤列。在做表达式时先明确“题目要求的是哪一行还是哪一列”再决定用哪个运算符。如果两个都要标准的做法是先选择再投影即π列(σ条件(R))。第二笛卡尔积忘写条件。写表达式时如果涉及到多张表一定仔细检查有没有遗漏连接条件。数据库执行计划里出现笛卡尔积往往意味着查询有逻辑问题。第三自然连接的结果列数敏感。由于自然连接会自动合并同名属性两张表公共属性一旦有额外业务含义不同的列就会产生错误。用自然连接之前一定要确认两张表的同名属性就是连接语义所依赖的那个属性。第四并、差运算的结构一致性。两个关系的属性必须完全对齐这在理论考试里一般不会出问题但在实际写SQL时尤其用UNION拼接报表时很容易出现列数不同、类型不一致的报错。6.2 常见面试题思路查询“选了课程1或课程2的学生”用并运算πsno(σcno1(SC)) ∪ πsno(σcno2(SC))。查询“选了课程1但没选课程2的学生”用差运算πsno(σcno1(SC)) − πsno(σcno2(SC))。查询“选了全部课程的学生”用除法SC÷C。判断两个关系代数表达式是否等价可以从选择下推、无关属性消除、连接交换律等角度分析。这些题目看似简单实际上每一道都在考察你对运算符语义的理解是否准确。比如“或”对应并集“并且”对应交集但交集需要转换为差互斥条件则可能需要用并和差组合。平时练习时可以自己随机构造几张表比如学生表、课程表、选课表然后对着题目写表达式再对照SQL执行结果验证。这个自检流程我在复习时用过效果非常好。6.3 从学习到应用的一点体会在我自己学习关系代数的过程中最大的感受是不要把它当作一门纯理论课程来背。它是数据库查询的“思考语言”是理解SQL语法、执行计划、查询优化这三者之间关系的一座桥。比如你在看执行计划时看到Nested Loop、Hash Join、Merge Join如果脑子里有关系代数的连接概念就能立刻意识到它们只是“实现连接的三种不同算法”看到执行计划里出现了Filter和Index Seek也能联想到选择运算下推的效果。这种“翻译”能力比单纯背下一百条SQL优化技巧更有长期价值。如果你正在做数据库课程设计或者准备数据库相关面试我建议你花几天时间把关系代数运算符逐个过一遍每个运算符自己造数据、写表达式、和SQL对照结果。这个过程本身不复杂但一旦打通你会发现原来很多SQL写法就不再是死记硬背而是可以自己推导出来的东西。最后再分享一个小技巧面试前可以自己整理一张“需求短语到运算符”的对照表比如“所有、全部”对应除法“存在、至少一个”对应半连接或并运算“不存在”对应反连接或差运算“同时满足”对应交运算。真正遇到题目时照着这张表去匹配思考速度和准确率都会明显提升。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价