ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

关系代数:从集合运算到SQL查询优化的底层思维

关系代数:从集合运算到SQL查询优化的底层思维 关系代数这个概念我第一次接触是在学校的数据结构课之后、数据库课的开篇部分。当时觉得它是一堆奇怪的希腊符号σ、π、⋈像数学课上用烂的字母换了个马甲完全不知道学它有什么用。直到后来在工作中真要手写复杂SQL、排查慢查询、设计报表查询逻辑时才回过味来——关系代数就是写SQL的底层直觉所有看似绕弯子的查询拆到关系代数这一层都清清楚楚。这篇内容我就以“关系数据库-08. 关系代数”为主线把从集合运算是怎么演变成select、join的为什么学者们要设计这套抽象语言以及实际排查问题时它怎么帮到我完整讲一遍。1. 关系代数到底在讲什么——为什么数据库课程几乎都要学它1.1 关系代数不是一门语言而是一套运算规则关系代数最早由E.F. Codd在1970年提出关系模型的时候一并定义下来。它研究的问题是如果你有一堆关系也就是我们常说的表你能怎么操作它们从而得到新的关系这些操作被设计成一套封闭的运算系统——操作的输出依然是一个关系所以你可以把多个操作用某种方式串起来形成一条“查询流水线”。听起来抽象但类比一下就很简单。想象你有一堆乐高积木关系代数定义的是积木之间可以怎么连接你可以把两堆积木合并并可以把相同颜色、相同凹槽的凸点对齐拼接连接可以只挑出某些颜色的积木选择可以只保留凸点朝上的那些面投影。乐高没有规定你最后必须拼出一辆车但给了你拼任何东西的基本动作。关系代数就是这套“基本动作”的严谨数学表达。它解决的是什么问题往大说它给出了“查询能力”的边界和衡量标准。往小说它让你手拿一张复杂的SQL能准确判断这条SQL背后的执行逻辑、能不能改写得更高效、哪里可能出错。1.2 学它最有用的三个实际场景很多初学者问我直接用SQL就行为什么还要学关系代数我工作后的切身体会至少有三个场景离不开它。第一个是SQL改写优化。判断两条SQL是否“等价”最稳妥的办法是先转换成关系代数表达式再做代数变换。比如WHERE a1 AND b2其实就是σ(a1 ∧ b2)(R)而根据选择操作的可交换性你可以把选择下推、先过滤掉大部分行这直接影响执行计划。第二个是理解不直观的复杂查询特别是带EXISTS、NOT EXISTS、ALL、ANY的嵌套子查询。这些语义在SQL里写出来绕好几层但一旦翻译成关系代数你会发现不过就是除运算、连接、差集的组合瞬间变得清晰。第三个是分析和设计数据库。在做数据流、数据血缘分析时关系代数提供了一种精确描述“数据怎么从源表流向目标表”的语言比用自然语言描述靠谱得多。一句话总结关系代数是数据库的“思维语言”SQL是它的“口语表达”。学透关系代数SQL就不再是死记硬背的句型库。2. 从集合论说起并、交、差与笛卡尔积2.1 关系本质上是一个特殊的集合关系数据库的“关系”这个词学术上指的就是一个二维表。但严格来说关系是一个集合它由若干元组行组成并且有两条关键约束每个元组的属性个数相同即表结构固定集合中不允许有重复元组即行不能完全相同。正因为它是一个集合所有经典的集合运算——并、交、差——都能直接用在关系上。这是理解关系代数最容易被忽视的基础很多人在学并集、交集时觉得理所当然但这里有一个隐藏条件两个关系要做到这些运算它们的属性集合必须相容通俗说就是“列名字和列顺序都对得上”。比如学生表学号姓名年龄和老师表工号姓名年龄直接做并集是没有意义的但如果你把老师表改名为学号姓名年龄且语义上认为“这里是两类人员”那它们就能做并集了。2.2 并、交、差表的加加减减假设我们有研究生选课登记表R1和本科生选课登记表R2结构完全相同都是学号课程号成绩。并运算R1 ∪ R2两个表中所有元组去掉重复后放在一起。它对应SQL里的UNION或UNION DISTINCT。如果我想知道“所有学生选了哪些课”用并集就能得到完整列表重复选同一门课的学生只出现一次严格关系代数不允许重复。交运算R1 ∩ R2两个表中都出现的元组。对应SQL里的INTERSECT。比如想知道“哪些选课记录既存在于研究生表也存在于本科生表”——虽然现实里这种情况比较奇怪但运算本身很好理解。差运算R1 - R2在R1中出现但不在R2中出现的元组。对应SQL里的EXCEPT或者NOT IN/NOT EXISTS。常用于找“有但没有的差集”。比如“选过课但考试成绩为空的记录”就可以用选课表减去有成绩的选课表得到。这里有个实际使用时经常踩的坑SQL的UNION默认去重所以如果原表本身有重复行虽然规范关系不允许但现实中许多临时表、外部表并没有唯一键并集结果的行数可能比两表行数之和少很多。而如果业务需求是“保留所有重复行”你得老实写UNION ALL这在关系代数里反而找不到完全对应的操作因为关系代数从定义上就不接受重复元组——这也是为什么实际系统里都允许用户用“多集”语义的原因。2.3 笛卡尔积连接操作的底层积木笛卡尔积可能是在数学课上就接触过的东西两个集合中所有元素两两配对。放在关系里R × S就是把R的每一行和S的每一行拼接成一个更宽的行行数相乘、列数相加。它本身很“暴力”大多数时候我们不想要全部组合只想要满足某种条件的组合。但千万别小看这个运算所有的连接操作都要先经过笛卡尔积再加上过滤条件。你写的每个JOIN ON在执行计划层面都有可能被转换成笛卡尔积加选择只不过优化器会用索引等手段避免真的生成那么巨大的中间结果。举个具体例子。老师表T有2行课程表C有3行T × C会得到6行每一行都是一位老师和一门课程的组合。如果业务是“所有老师所有课程的可能任教关系”这张笛卡尔积就是答案。如果我只想看“老师实际教的是什么课”那还得在工作表T_C里去匹配这样就变成了连接。所以务必记住笛卡尔积是一个“全组合”操作它本身很有价值但它同时也是连接操作的物理来源理解它才能理解为什么连接一不小心会变成全表扫描的“灾难性”笛卡尔积。3. 选择与投影最常用的行与列操作3.1 选择σ按行过滤的精确刀法选择运算是关系代数里最直观的操作从一个关系里挑出所有满足条件为真的元组。记作σ条件(R)例如σ成绩 ≥ 90(选课表)就是从选课表中得到所有成绩不低于90分的记录。它的要点有三个。第一选择的行不改变列结构。输出关系与输入关系拥有同样的属性集合做选择的目的就是筛行。第二选择条件里可以出现算术比较、逻辑与、逻辑或、取反。比如σ成绩 ≥ 90 AND 学分 3(选课表)。这对应SQL里的WHERE子句完全是一一对应的。第三选择的“下推”是查询优化里最重要的改写手段。因为选择和选择之间可以交换位置也可以在某些情况下和连接交换位置。比如查询“选了‘数据库’课程并且成绩大于90的学生”SQL里常写成先做连接再筛选SELECT student.name FROM student JOIN choose ON student.id choose.student_id JOIN course ON choose.course_id course.id WHERE course.name 数据库 AND choose.score 90;这个SQL的执行计划很可能被优化成“先对课程表做选择只保留数据库课程再对选课表做选择只保留高分最后连接”——这就是选择操作下推到连接之前。关系代数理论告诉你这样的改写得到的结果和原先完全一致因为选择与连接是可交换的。很多数据库优化器内部做的就是这种操作。实际排查慢查询时我经常先看执行计划里的filter和join predicate如果发现发现一个选择操作被放到连接之后才过滤大量行就会思考能不能手动改写SQL让过滤提前。这就是用关系代数思维反过来指导写SQL的经典例子。3.2 投影π按列裁剪的瘦身术投影运算记作π列名列表(R)表示从R中只保留指定列同时去除重复行。例如π学号, 姓名(学生表)就是得到一张只有学号和姓名两列的表。投影的两个细节非常容易踩坑投影结果会自动去重。关系代数里关系是集合不允许重复元组所以如果我只投影一个列“学院”那么同一学院只会在结果中出现一次。如果业务上想知道“有哪几个学院”用投影恰好合适但如果我想得到“每个学生的学院信息并保留重复”这种查询在纯关系代数里其实无法直接表达因为你就得保留学号列这样就不会去重了。投影对列的裁剪会影响后续操作。比如先投影掉一些列之后再做连接被投影掉的列就永远消失了。所以在写复杂查询时投影的位置非常重要经常需要在“提前裁剪数据和保留后续需要的字段”之间权衡。投影对应SQL里的SELECT DISTINCT 列名而普通SELECT 列名不去重背后关系模型和实际数据库的差异就在这SQL里的表是多集关系是集合。3.3 改写查询的第一步把SQL拆成σ和π这里给你一个非常实用的练习方法拿到一条查询先别急着看执行计划尝试手写它的关系代数表达式。比如SELECT course_id FROM choose WHERE score 60 GROUP BY course_id;先不用管GROUP BY简化后可以写成πcourse_id(σscore 60(choose))。看到没有一条SQL的核心抽取出来就是先选行、再取列。再想复杂一点如果加了HAVING COUNT(*) 5就涉及分组和聚集运算了这在纯关系代数里需要扩展到“分组-聚集”代数许多教材会补充这个操作。不过核心的两个“看家本领”一直就是σ和π。记得我刚开始带新人时常有人问为什么一条带大表连接的SQL那么慢。我带着他们把SQL拆开逐层标注哪个部分对应哪个σ、哪个π跑一两个小时新人自己就明白了看似短小的SQL内部可能是两次笛卡尔积三次π两次σ堆叠出来的。一旦你能做到这种拆解查询优化的思考就有了落脚点。4. 连接操作自然连接、条件连接、等值连接4.1 连接的本质把多个表横向拼接起来连接可能是关系代数里最常用、最考生动的一个操作。它把两个关系的元组按某种条件组合成新元组形成一个列数更多的新关系。生活中无时无刻不在用学生表和选课表连接得到每个学生选了哪些课订单表和商品表连接得到每笔订单的商品详情。连接的核心是“匹配”。沿着这个思路连接可以分为几类关键区别在“匹配条件”怎么定义。4.2 等值连接与条件连接大小比较、范围匹配条件连接是最一般的连接。记作R ⋈θS其中θ可以是任何比较运算符例如、、、、等。条件连接从R和S的笛卡尔积中取出满足θ条件的所有元组。比如有员工薪资表Salary员工ID薪资和职位等级表Level最低薪资最高薪资职位名称想给每个员工配上对应的职位等级就得用S.salary L.min_salary AND S.salary L.max_salary这种范围判断这就是条件连接。等值连接是θ连接的特例条件是“等号”即R和S中某些属性值相等。实际开发中最常见的JOIN ON R.id S.uid就是等值连接。数据库索引对等值连接最友好因为有哈希连接、排序合并连接、索引嵌套循环连接等一堆执行算法可以选。而非等值连接往往只能扫全表或走范围索引。4.3 自然连接⋈去掉重复属性的贴心朋友自然连接是一种特殊的等值连接先按两表中所有同名属性做等值匹配然后在结果中去掉重复的同名列。举个例子假设有学生表student学号, 姓名和选课表choose学号, 课程号, 成绩两表都有“学号”这一列。自然连接student ⋈ choose会等价于在学生表和选课表上使用student.学号 choose.学号做连接然后结果只保留一列“学号”共四列学号、姓名、课程号、成绩。自然连接很优雅但现实世界很少直接用——因为真实表设计时经常有不同的表用不同的属性名表示同一个意思比如“学号”在学生表里叫sid在选课表里叫student_id同名同义属性反而少见。所以SQL里对应的写法不是非常简单必须显式写USING或ON。不过在教材习题、概念题里自然连接是常客理解它的关键就是“所有同名属性都参与等值匹配并且结果里同名属性只保留一列”。4.4 自连接一个表与自己连接的本质还是连接还有一个高频考点是自连接把同一张表当成两个不同的关系来做连接。比如要查出“同一门课中成绩比课代表高的学生”需要拿选课表和选课表本身做比较。自连接完全可以用关系代数的连接运算表达给同一个表起两个不同的别名就相当于两个“副本”。在SQL里写SELECT a.student_id, b.student_id FROM choose a JOIN choose b ON a.course_id b.course_id AND a.score b.score它对应的关系代数严格来说是把choose复制一次一个记作R1一个记作R2再做连接。理解这点对调试复杂查询特别重要自连接并没有新运算它只是连接的重复使用。4.5 连接时最常见的“多条件”组合现实里连接经常不止一个条件比如按两个字段匹配ON a.订单号 b.订单号 AND a.渠道 b.渠道。这在关系代数里就是条件连接里θ条件是一个复合条件两个等式用AND连接。执行计划上优化器可能会把这种多列等值连接用多列索引一起处理比单条件连接效率更高。有个小技巧如果你写了多个JOIN ON试着把所有条件都列出来标出哪个属于等值、哪个属于范围条件。很多慢查询的根源就是把范围条件写在了WHERE里而没写在ON里导致优化器错失良机——这虽然属于SQL写法优化但背后其实就是对连接条件结构不够清晰。5. 除运算解决“至少包含”类查询5.1 为什么要有除运算一类特殊查询无法用前三类表达并、交、差、笛卡尔积、选择、投影、连接这些运算已经能表达很多查询了但有一类查询它们很难直接表达“找出满足所有条件/包含所有项的那些值”。比如找出选修了全部课程的学生。找出拥有全部商品类别的供应商。找出在所有月份都有销售额的销售员。这类查询的特点是外层实体学生、供应商、销售员需要和一个“全部集合”全部课程、全部商品类别、全部月份做匹配要求包含关系。只靠连接、选择、投影写出来嵌套层数非常多而且难以通用化。于是关系代数专门引入了一个除法运算记作R ÷ S。5.2 除运算的定义用“余数”来理解除运算的定义比较高冷设关系R有属性集合A关系S有属性集合BB是A的子集。R ÷ S的结果包含所有位于R中但不在S中的属性记作集合C并且结果里的每个元组t与S中每一个元组s拼接后所得元组都必须出现在R中。用白话翻译一下R ÷ S 是“在R中出现的、与S中所有元组都匹配过”的那些元组的C部分。换句话说对于某个C属性组合值t如果R里所有“属于S的那几个属性值”都覆盖了S的全部组合t就出现在结果中。举例是最直观的。假设选课记录表R学号课程S1数学S1语文S1英语S2数学S2语文S3数学另一张表S要求修的课程课程数学语文那么R ÷ S 的结果是哪些学号我们来逐一验证。学号S1选了数学和语文还有英语包含了S中全部课程数学、语文所以S1在结果中。学号S2选了数学和语文也包含了全部所以S2也在。学号S3只选了数学缺少语文不在结果中。因此R ÷ S的结果是学号S1、S2。这个例子虽然简单但已经很清晰地展示了“至少包含全部”的语义。如果S表变成了“数学、语文、英语”那只有S1能进入结果因为S2没有英语。5.3 除运算与SQLNOT EXISTS 的经典写法除运算在SQL里最常见的等价写法是“双重NOT EXISTS”。比如“找出选修了全部课程的学生”SQL可以写SELECT student_id FROM student WHERE NOT EXISTS ( SELECT 1 FROM course c WHERE NOT EXISTS ( SELECT 1 FROM choose ch WHERE ch.student_id student.student_id AND ch.course_id c.course_id ) );这段SQL的逻辑是对于每一个学生检查是否不存在一门课程该学生没选。如果没有这样的课程就说明他选了所有课程。用关系代数的语言来说就是π学号(选课表) ÷ π课程号(课程表)。一旦你把这个对应关系印在脑子里以后再见到“全部”“所有”“每一个都没落下”这类查询第一反应就是除运算然后秒写出NOT EXISTS模板。5.4 不用除运算用连接和差集组合也能实现如果你在笔试或面试时遇到了除运算但一时不知道那个符号怎么写别慌。除运算可以用其他运算等价表达。公式如下R ÷ S πC(R) - πC( πC(R) × S - R )理解这个公式的思路是先取R中所有不同的C值即所有可能的“候选者”记作A πC(R)。取A和S的笛卡尔积得到所有“候选者 × 全部要求项”的组合此时每个候选者拥有完整的S部分。用这个组合去减去R差集中的元组表示“这个候选者缺少了某个要求项”。再从这些“缺少项”中取C部分得到“不完全满足条件的候选者”。用A减去第4步的结果剩下的就是“完全满足条件的候选者”。我第一次看到这个公式时也绕了很久后来当成“缺项查找法”就顺了先制造一个“理想完整表”和真实情况做差看看谁有缺项最后排除有缺项的人。这样理解考试时就算忘了除法也可以基于连接、投影、差集自己推导出来。5.5 除运算在实际业务中的价值虽然日常写业务查询很少会直接用到除运算符号但它的思想贯穿大量报表需求。比如“用户拥有全部权限”“某角色包含全部菜单”“一个订单状态流转中必须经过全部节点”等等。一旦你练熟除运算的识别遇到这类需求时就能直接写出SQL而不是一层又一层地套子查询。我印象最深的一次是在做数据权限设计时需求是“找出同时拥有A、B、C三个数据域权限的所有用户”。数据表是user_accessuser_id, domain_id而domain_id从权限字典里查。当时团队里新同事写了三层JOIN和多个EXISTS非常绕。我让他们先用除运算思考其实就是user_access ÷ 权限域表里的那三个domain_id。然后用NOT EXISTS模板两分钟写完正确性也一目了然。6. 关系代数与SQL的对照为什么你在写SQL时其实在用关系代数6.1 常用运算对照速查表这里整理一份对照表平时写SQL时可以对照着看。SQL关键字/子句关系代数示例SELECT 列π列SELECT name FROM student πname(student)WHEREσ条件WHERE score90 σscore90(choose)JOIN ON⋈条件或自然连接JOIN ON s.idc.sid student ⋈s.idc.sidchooseUNION∪SELECT ... UNION SELECT ... R1 ∪ R2INTERSECT∩SELECT ... INTERSECT SELECT ... R1 ∩ R2EXCEPT-SELECT ... EXCEPT SELECT ... R1 - R2笛卡尔积×FROM a, b a × b双重NOT EXISTS÷... R ÷ SGROUP BY HAVING分组-聚集扩展GROUP BY course HAVING COUNT()5 γcourse, COUNT() (choose)会话里看到没有SQL的每一个基础子句都能找到关系代数对应的运算甚至SQL的执行优化器也是基于关系代数的等价变换规则来设计执行计划的。6.2 为什么优化器能自动把SQL改得很高效因为关系代数的等价变换数据库优化器最核心的工作之一就是“逻辑优化”把SQL翻译成关系代数表达式然后基于一套“等价变换规则”不断改写直到出现一个预期代价更低的表达式再进入物理优化步骤。常见的等价变换规则有哪些呢举几个最常用的选择串接定律σc1(σc2(R)) σc1 ∧ c2(R)。多个选择可以合并成一个选择条件用AND连接。选择对投影的分配律在某些条件下选择可以与投影交换顺序。比如先投影再选择如果选择不涉及被投影掉的列那顺序无所谓如果选择涉及了被丢弃的列就不能随意交换。连接与选择的交换律σc(R ⋈ S) 在c只涉及R的列时可以改写为(σc(R)) ⋈ S。这就是“谓词下推”的理论基础。连接结合律与交换律R ⋈ S S ⋈ R(R ⋈ S) ⋈ T R ⋈ (S ⋈ T)。优化器据此调整连接的执行顺序优先连接数据量小的表。就是不理解这些规则你也能写SQL但如果你不理解优化器“替你”选择的执行计划你根本读不懂遇到慢查询时只能干瞪眼。我自己调试过一条非常复杂的报表SQL原始写法是三层嵌套子查询加两个LEFT JOIN。我把它逐字翻译成关系代数表达式然后发现内层子查询本可以对一张小维度表先做选择、再做投影结果因为子查询被“封装”得太死优化器无法将选择下推整条SQL跑了60多秒。后来手动把子查询提出来先过滤再参与连接同样逻辑的SQL跑到了0.3秒。这就是关系代数知识直接变成性能收益的真实体验。6.3 写SQL时应该先想关系代数还是先想最终结果我的建议是先想关系代数再写SQL。尤其当你面对一个复杂查询时别急着敲键盘。先在纸上画出各个关系标出需要的列、条件、关联键然后判断需要哪些运算、顺序是什么。有些人觉得这太“学院派”但我自己经过无数次踩坑后很确信思维上的提前拆解省下的时间远大于直接上手写SQL反复调试的时间。到现在我带新人做数据仓库的ETL任务第一课就是让大家把需求文档翻译成关系代数表达式哪怕只有一个join也要写出来。理由很简单如果连关系代数这一层都理不清后面写出来的SQL大概率是“试出来的”正确性全靠测试兜底一旦数据分布变化就可能出错。7. 常见问题与排查技巧实录7.1 问题1自然连接结果列数怎么算做习题或者写代码中经常遇到这个问题R有m列S有n列如果它们共有k个同名属性那么自然连接结果的列数是 m n - k。例如student(4列)和choose(5列)共有学号这一列k1自然连接结果是45-18列。如果你发现某个自然连接结果列数和你预想的不一样先检查两表中到底有几个同名属性——只要有两个同名你就要注意了因为自然连接会把所有同名属性都做等值匹配不是只挑一个。比如学号在两个表中都叫id而且两个表中还都有一个叫做state的列那自然连接会要求id相同 AND state相同才能连接成功。这种隐性条件极易导致行数骤减排查时先把列数算清楚。7.2 问题2空值NULL带来的麻烦关系代数在标准定义里不支持NULL。但实际数据库里NULL遍地都是。这就导致理论关系代数和SQL之间存在偏差最典型的就是选择运算σ成绩90(R)会把成绩为NULL的行丢掉因为NULL90既不是真也不是假SQL的WHERE也一样会过滤掉NULL。很多人在统计异常数据时才发现明明选择条件是“成绩90”但NULL行也没了他们以为这是数据库bug其实是集合语义在作祟。解决技巧很简单如果业务上认为NULL也是一种“状态”那就得在SQL里显式写OR score IS NULL。在关系代数层面你其实是在把条件扩展为(score 90 OR score IS NULL)但严格意义上“IS NULL”并不是关系代数标准运算符而是一种补充运算。理解这种偏差才会比新手多留个心眼。7.3 问题3属性歧义与重命名ρ进行连接时如果R和S都有“学号”直接写成σ学号学号(R × S)会让人糊涂到底哪个是R的哪个是S的这就需要用**重命名运算ρ**给属性改名。比如ρ(学号→学生学号)(S)把S里的“学号”改成“学生学号”然后在连接条件里写R.学号 S.学生学号。在SQL里对应的是使用别名aliasFROM student AS a JOIN choose AS b ON a.id b.student_id没有别名你根本写不了自连接。所以见到ρ运算不要慌它就是数据库内部处理“同名冲突”的标准手段。写复杂SQL时给每个表起清晰别名、避免同名列掉进连接陷阱是非常良好的习惯。7.4 问题4除运算结果出现多余的列许多初学者在使用除运算时会误把R中多余属性不在S中的属性也放进结果。这里记得R ÷ S结果只保留不属于S的那部分列。比如R是(学号, 课程, 授课教师), S是(课程)那么R ÷ S的结果属性只有“学号”不包含“授课教师”。如果你想要同时保留教师的某种信息需要在除法之后再和原R做连接或投影。这个错误我在笔试和实际审核SQL时都常看到。根本原因是没有厘清“哪些属性是被除数表的共有属性哪些是商结果属性”。多算一步即可。7.5 问题5如何通过关系代数判断两条SQL是否等价有这个疑惑的时候不妨把两条SQL都翻译成关系代数表达式然后看是否能通过等价规则从一种变换到另一种。比如A查询先把A表过滤再连B表B查询先连B表再一起过滤只要过滤条件只涉及A表这两个表达式就是等价的优化器会选成本更低的那个。实际排查时我通常还会把表达式画成“算子树”的样子不画图也行就是层级式地写出来每层的输入、输出都是什么关系。发现两条表达式最大的差异点在哪是不是差了一个选择下推自然就知道哪条更高效了。7.6 我的实操小技巧写一个“关系代数翻译本”结合我带过的不少新人他们学关系代数最大的障碍是“符号太多记不住”。其实不需要死记硬背关键是把这个表当作你的翻译器遇到不熟的查询先查表找到关系代数表达再对照SQL写出来。每次处理一个需求时都做这个动作我在一周内就熟练了。另外一个非常推荐的方法把工作中常用的5到10个查询模板都翻译成关系代数比如“动态条件查询”“一对多连接后去重”“存在性判断”“集合差集”等。你会发现模板背后的关系代数结构惊人的相似。当你面对一个全新需求时第一步就是在脑中搜索最接近的关系代数模板然后在此基础上修改比从头写SQL快很多。说到最后我分享一下个人体会。关系代数从一开始让人头疼的希腊符号堆叠到后来变成我优化SQL、设计查询逻辑时离不开的思考框架中间也就隔着一层“愿不愿意拿它当工具”的距离。很多人觉得它是纯理论、考试完就忘其实它是数据库理解力的底盘之一。每当我遇到复杂查询或慢查询我都会下意识地问自己现在这个查询它的关系代数结构是什么样的选择、投影、连接、除运算分别落在哪一层往往捋到这一层问题就已经解决了一半。如果你现在刚开始学关系代数建议别抱着“应付考试”的心态去学试着在每条SQL后面写出它的代数结构。用不了几次你就会发现数据库查询的世界原来这么通透。
返回列表