FEATURED · 精选文章

关系代数从入门到实战:从集合运算到SQL查询优化

发布时间 / 2026/9/18 17:00:19
来源 / 创域科博编辑部
栏目 / 资讯中心
关系代数从入门到实战:从集合运算到SQL查询优化 每次讲到关系代数总有人问我同一个问题“这玩意儿除了考试还能干啥SQL不香吗”说实话我当年学《数据库系统概论》时也这么想过直到后来自己写复杂查询时被嵌套子查询绕晕回头用关系代数一拆解才发现原来“翻译成SQL”这件事本质上就是把关系代数表达式一句句换皮。关系代数不是数据库理论里的老古董而是你理解查询优化器、写出高效SQL的底层直觉。这篇内容我会把关系代数的所有核心运算拆开揉碎从集合运算到除法每个都配上可手算的实例再带你从自然语言一步步搭出关系代数表达式最后落到SQL映射和查询优化上看完你就能把这张“理论地图”装进脑子里。1. 关系代数到底在解决什么问题1.1 先把“关系”这个词说清楚关系代数里的“关系”指的就是我们在数据库里看到的那个二维表。表的每一行叫元组每一列叫属性表名加属性集合就构成了关系模式。比如一张学生表Student(Sno, Sname, Ssex, Sage, Sdept)这行字的意思是有一张叫Student的表它有学号、姓名、性别、年龄、所在系这五个属性这就是关系模式。而表里实际存的每一行比如(2024001, 张三, 男, 20, CS)就是一个元组。为什么叫“代数”因为它是把表当作运算对象用运算符从已知表推导出新的表。就像小学时用数字做加减乘除关系代数就是用关系做运算。你输入两个关系经过某种运算输出一个新关系。这就意味着任何关系代数运算的结果依然是“一张表”依然符合关系的所有性质因此可以进行嵌套和继续运算——这种封闭性正是它能作为查询语言理论基础的根源。1.2 两类运算的划分逻辑关系代数的运算符可以分为两大类。第一类是传统集合运算包括并∪、差−、交∩和笛卡尔积×。这类运算来源于数学里的集合论操作的对象是“关系里的元组集合”它们要求参与运算的关系具有相容性也就是说属性个数相同、对应属性的类型一致。想象你把两张表上下叠在一起列结构一样才能做并、差、交这个直觉在后面会非常有用。第二类是专门的关系运算包括选择σ、投影π、连接⋈和除÷。这类运算才是数据库查询真正依赖的“灵魂”因为它们是针对表的行、列结构专门设计的。选择管行投影管列连接管跨表组合除管“全体满足”这类语义。这两类运算组合在一起就能表达几乎所有你想对数据库执行的查询。而且在关系代数里一个查询就是一个表达式就像算术里一个计算就是一个算式。这种“表达式”的特质给查询优化带来了可能同一个查询意图可以写出多个逻辑等价的关系代数表达式优化器从中挑一个执行代价最小的。后面我会专门讲这一点。2. 集合运算并、差、交、笛卡尔积2.1 三个小运算并、差、交先看并运算。R∪S返回属于R或属于S的所有元组重复元组只保留一份。注意这里“重复元组会去掉”是关系模型的规定因为关系是集合集合里不允许重复元素。实际数据库表一般允许重复行所以当你要在SQL里表达“并去重”时用的不是UNION ALL而是UNION。并运算的适用前提是R和S相容。举个例子R是计算机系的学生S是软件工程系的学生两个关系的属性都是(Sno, Sname, Ssex, Sage, Sdept)那么R∪S就是这两个系所有学生的并集去掉了那些同时出现在两个系记录里的重复行虽然现实里一个人不会同时属于两个系但集合运算逻辑上要这样处理。差运算R−S返回属于R但不属于S的元组。用集合的直觉来理解从R这张表里把S里出现过的行全部划掉剩下的就是结果。差运算最典型的用途是实现“排除”类查询比如“查计算机系但年龄不大于20岁的学生”逻辑上就是先选计算机系学生再排除掉年龄≤20的学生。注意R−S和S−R不是一回事前者保留R里独有的后者保留S里独有的顺序不能反。交运算R∩S返回同时属于R和S的元组。它可以用差运算表达R∩S R − (R − S)。交集用到的场景相对少一些但它和并、差一样都是SQL中INTERSECT、EXCEPT对应的理论基础。很多同学在写“查同时选修了A课程和B课程的学生”这类查询时会本能地想到用WHERE CnoA AND CnoB这当然是错的正确的思路就是用交集或者分组聚合。关系代数帮你把这一步逻辑预先想清楚了。2.2 笛卡尔积数据库里最容易翻车的运算笛卡尔积R×S的规则很简单R的每一行和S的每一行都拼在一起形成新行。若R有m行、n个属性S有p行、q个属性那么R×S有m×p行、nq个属性。听着简单但实际使用中它是性能和语义的双重陷阱。假设学生表有1000行选课表有5000行不写连接条件直接做笛卡尔积就是一个500万行的中间结果很多新手写SQL时忘了JOIN的ON条件后台瞬间就被这种“隐式笛卡尔积”打爆。关系代数的好处是在表达式层面就把这种风险摊开给你看凡是出现了×后面几乎必然跟着一个选择运算来“收窄”它。笛卡尔积的意义在于它为连接运算提供了“原料”。等值连接本质上就是“先做笛卡尔积再按指定列相等的条件做选择”。所以你在关系代数里看到R×S别慌它往往不是一个最终结果而是组合操作的中间环节。2.3 算基数、算结果、检验你是否真懂的小题来一道经典的入门题。设R(A, B)有两行{(1, a), (2, b)}S(B, C)有两行{(a, x), (b, y)}。R∪S不行因为两者属性不同R是A、B两列S是B、C两列不满足相容性所以不能直接并。R×S得到4行22列{(1,a,a,x), (1,a,b,y), (2,b,a,x), (2,b,b,y)}。如果对R×S做选择σ(R.BS.B)则得到{(1,a,a,x), (2,b,b,y)}这就是一次等值连接的结果。这个例子建议你亲手在纸上画一遍把每一步中间表都写出来。很多同学看公式觉得懂一让手算就懵就是因为没有真正“搬运”过数据。关系代数的学习动手演算比看十遍书都管用。3. 选择与投影查询操作的“地基”3.1 选择行级别的过滤器选择的符号是σ读作sigma语法是σF(R)。F是选择条件比如“SdeptCS”、“Sage20”、“SdeptCS AND Sage25”。它的语义非常直观从R中挑出满足条件F的整行列结构保持不变。你可以把它理解成Excel里的“筛选”或者SQL里的WHERE子句。选择运算有几个性质要特别留意。第一选择条件是命题公式由原子条件和逻辑连接符AND、OR、NOT构成。原子条件形如“属性名 比较运算符 常量/属性名”比较运算符有、≠、、≥、、≤。写条件时要注意属性名是否存在于R中否则条件无意义。第二选择运算满足串接定律σF1(σF2(R)) σF2(σF1(R)) σF1∧F2(R)。这意味着你想要“计算机系且年龄大于20”的学生既可以写成σ(SdeptCS AND Sage20)(Student)也可以先选系再选年龄或者反过来。这个性质在查询优化里很常用——它允许你把选择条件拆开、下推从而尽早缩小中间结果。第三选择结果的元组顺序是不确定的。关系是集合没有顺序概念。你在写关系代数表达式时不需要关心顺序但如果你是一个习惯看SQL执行结果的人可能会觉得“没顺序很难受”。没关系SQL里的ORDER BY是为展示服务的和关系代数的语义层无关。实际做题时最容易错的地方是条件里用错了属性名或者把AND写成了OR。还有一个低级但常见的错误选了行以后还想要原来的所有列却忘了选择不会改变列集合它只筛行根本不需要你指定列。你要是写出σ(Sno, Sname, SdeptCS)(Student)那说明你把选择当成了“选行选列”混合体这个误区一定要纠正。3.2 投影列级别的裁剪器投影的符号是π语法是πA1,A2,...,Ak(R)A1到Ak是你想保留的属性名。它的作用是从R中选出指定的列并去掉结果中的重复行。去掉重复行这五个字是投影运算最容易被忽略、也最容易在题目里挖坑的地方。假设有一个关系选修SC(Sno, Cno, Grade)你想知道有哪些学生选过课写了πSno(SC)。如果有一个学生选了三门课他在SC里有三行但学号相同那么投影之后这三行在“学号”这一列上完全相同按照集合的规定结果里只保留一个。所以πSno(SC)直接给出了所有选过课的学生学号天然去重。你后续要做计数、做是否存在判断时这个去重很可能就是你想要的行为。投影也可以参与嵌套。比如先选择后投影先筛选符合条件的行再从这些行里挑列这是关系代数表达式里最常用的组合模式πSname(σ(SdeptCS)(Student))的意思是“先找计算机系学生再输出他们的名字”。注意顺序不能反如果你先投影掉Sdept列后续就没办法用Sdept做选择了。这就是为什么在构造表达式时你得想清楚每一步之后还剩下哪些列。3.3 选择投影组合的经典用法这类“先选行再选列”的模式覆盖了数据库日常查询里一大半的需求。我举三个例子查询1查所有年龄小于18岁的学生的姓名和所在系。 π(Sname, Sdept)(σ(Sage18)(Student))查询2查计算机系男学生的学号。 π(Sno)(σ(SdeptCS AND Ssex男)(Student))查询3查成绩大于90分的选课记录只要学号和课号。 π(Sno, Cno)(σ(Grade90)(SC))这三个表达式的共同套路是先σ筛选行再π裁剪列。你在做关系代数题时拿到查询需求先想清楚“条件落在哪些属性上结果要哪些属性”然后用σ和π把行和列分别收拾干净。还有一个细节表达式的顺序会影响中间结果大小。如果一张表有10万行你要先做投影到2列再做选择和先做选择再投影前者的中间结果可能是后者的几十倍。这就是为什么数据库优化器喜欢把选择下推原则是“先缩行、再缩列”尽量让每一步中间结果变小。等你以后看执行计划会经常见到这个思路的具象化。4. 连接运算与它的亲戚们4.1 连接的本质是什么连接运算⋈是关系代数里最常用也最需要理解透的运算。它的定义可以拆成两步第一步对两个关系做笛卡尔积第二步按连接条件从笛卡尔积中筛选出符合条件的元组。所以连接可以表示为R ⋈F S σF(R × S)F就是连接条件比如R.Sno S.Sno、R.Sdept S.Sdept等。能从定义这个角度看连接你就能理解为什么连接操作会带来性能开销——它天然涉及“乘法级”的组合。实际执行时数据库当然不会真的先算一个巨大的笛卡尔积再筛选那样太慢了它会用索引、Hash Join、Nested Loop等算法优化。但从语义层面连接就是“笛卡尔积选择”这个等价关系在推导表达式时经常用到。4.2 等值连接与自然连接一个关键区别当连接条件是“属性值相等”时比如R.A S.B就叫等值连接。等值连接结果里会包含两个相连的属性列R.A和S.B是两列它们的值在每一行里是相等的但两张表的属性名可能相同也可能不同结果都保留。自然连接是等值连接的一个特殊形式它要求两个关系在“同名属性”上做等值比较并且结果中去掉重复的同名列。来看例子。R(A, B, C)有三行{(1, a, x), (2, b, y), (3, c, z)}S(B, C, D)有两行{(a, x, 10), (b, z, 20)}。R和S的同名属性是B和C。自然连接R⋈S要求R.B S.B且R.C S.C第一行R(1,a,x)Ba、Cx正好匹配S中的(a,x,10)所以连成(1,a,x,10)。 第二行R(2,b,y)Bb、Cy和S中的(b,z,20)不匹配y≠z所以丢掉。 第三行R(3,c,z)没有匹配丢掉。结果就是{(1, a, x, 10)}只有一行。如果用等值连接σ(R.BS.B AND R.CS.C)(R×S)得到的结果行和自然连接内容一样但列结构不同——它保留了R.B、R.C、S.B、S.C四个属性列总共6列。而自然连接会自动合并同名的B和C只留下(R.A, B, C, D)四列。很多教材和考试会专门问“自然连接和等值连接的区别”核心差异就两条第一自然连接必须在同名属性上进行等值比较等值连接不一定同名第二自然连接结果去掉重复列等值连接保留所有列。你做题时只要对比这两点基本不会错。4.3 外连接保留“落单”的行普通连接只保留能匹配上的行匹配不上的行直接被舍弃。但有些查询需要保留“落单”的行这就引出了外连接。左外连接R⟕S结果保留R中所有行S里没有匹配的属性用NULL填充。 右外连接R⟖S保留S中所有行R里没有匹配的属性用NULL填充。 全外连接R⟗S保留两侧所有行。举一个我经常用来教学的例子。Student表有张三、李四、王五SC表记录了他们选课的情况其中王五一门课都没选。那么Student⟕SC的结果里张三和李四正常显示选课记录王五那一行Sno、Sname正常但Cno、Grade是NULL。这个场景对应SQL就是LEFT JOIN也是很多人在实际业务里天天用的东西。关系代数里外连接的价值在于它给了你一种“保住主表全部行”的明确表达方式。考试里外连接一般会结合“查没有选课的学生”来考先做左外连接再找SC属性为NULL的行。另一种连接叫半连接R⋉S它在R中返回能与S中某行匹配的那些行只返回R的列而且不重复。半连接的用途主要在分布式数据库优化里用来减少数据传输量。考试大纲不见得要求半连接但你当扩展了解一下没坏处——它和“IN子查询”语义高度相似。5. 除法运算最抽象但最“考题”的运算5.1 除运算的定义拆解除法是专门的关系运算里最难理解的一个很多学生一看到R÷S就懵。我建议你用“披萨切分”的类比来破冰R÷S的意思是从R里找出那些“拥有S所要求的所有特征”的元素每个元素作为一行输出。形式定义是这样的设关系R(X, Y)和S(Y)其中Y是两者的共同属性。R÷S的结果是这样一个关系它包含所有在R中出现、并且其Y部分包含了S中全部Y值的X值。说得再直白一点R÷S πX(R) − πX(πX(R) × S − R)。这个等价表达式很关键它说明除法不是一个“凭空出现”的运算而是可以用投影、笛卡尔积和差来表达的。如果你在考试里忘了除法的计算步骤也可以用这个公式逐步推。5.2 用“选修了所有课程”来理解经典的除法例子永远是学生选课。设有SC(Sno, Cno)Course(Cno)关系代数表达式πSno,Cno(SC) ÷ πCno(Course)这个式子的含义是找出那些选修了Course里全部课程的学生学号。为什么除法能做到因为除法的语义就是“R中的X值必须覆盖S中所有的Y值”。具体推演一下假设SC表Sno Cno 001 C1 001 C2 002 C1 003 C1 003 C2 003 C3Course表 Cno C1 C2 C3那么SC ÷ Course的结果是003因为003选过C1、C2、C3覆盖了Course的所有课程001缺C3不满足002只选了C1更不满足。结果就一行003。这个例子会出现在几乎所有数据库教材的习题里因为“全部”这个量化词全称量词在关系代数里没有直接的运算符而除法恰好把“对所有的YX都满足”这个语义装进去了。5.3 除运算如何用其他运算表达以及它的使用边界用公式表达R÷S πX(R) − πX(πX(R) × S − R)。我来解释一下这个公式在做什么。第一步πX(R)得到所有出现过的X值第二步πX(R) × S是把所有这些X值都配上S的全部Y值形成一个“理想化的完整组合表”第三步减去R找出那些“应该在但实际不在R里的组合”第四步再投影出X得到的就是“缺失了某些Y的X”最后用全体X减去这些缺失的X剩下的就是“Y全部覆盖的X”。按这个公式也可以手算出上面例子的答案你可以自己走一遍。使用边界上要注意做除法时S的所有属性必须都包含在R里否则没法比较。而且除法只对“全称量词”类查询有效。像“查选修了全部课程且成绩都及格的学生”除法的基本思路一样只是要先把SC里成绩不及格的记录剔除掉再做除法。除法和“双重否定”NOT EXISTS嵌套在SQL里是等价的这个映射关系在后续查询练习里非常有用。6. 从查询需求到关系代数表达式完整实战6.1 先建立一张“翻译对照表”拿到自然语言查询需求怎么一步步写成关系代数表达式很多初学者卡住的原因是不知道从哪里下手。我的建议是先做一个思维转换把每个查询需求拆成“我要筛什么行、取什么列、需要拼哪几张表”三个问题。下面是常见查询短语和运算的一一对应建议你贴在桌前查询需求对应运算条件是某列的取值σ选择结果只要某些列π投影需要多张表的数据连接⋈或笛卡尔积×去掉重复行投影自带去重满足任一条件OR同时满足多个条件AND属于A但不属于B差−“全部”或“所有”除法÷保留主表所有行外连接⟕/⟖/⟗6.2 四个经典查询边做边讲继续用学生选课数据库Student(Sno, Sname, Ssex, Sage, Sdept)Course(Cno, Cname, Cpno, Ccredit)SC(Sno, Cno, Grade)。查询1查询“张三”选修的所有课程号。 思路先找出张三的学号再到SC里找记录。两步走 πCno(σ(Sname张三)(Student ⋈ SC)) 这里用连接把Student和SC拼起来然后按姓名筛选最后取课程号。如果先把Student选到只剩张三再和SC连接理论上结果一样。 πCno(σ(Sname张三)(Student) ⋈ SC) 这个写法其实更优化因为连接前已经把Student缩小到一行了。这个“先缩行再连接”的思想在后面讲优化器时也是核心。查询2查询选修了“数据库”课程且成绩在90分以上的学生姓名。 πSname(σ(Cname数据库 AND Grade90)(Student ⋈ SC ⋈ Course)) 三步连接后一起过滤表达了“学生、选课、课程”三张表的关联关系。运用连接和选择一行表达式搞定。查询3查询没有选修任何课程的学生姓名。 πSname(Student ⟕ SC) − 不对这里有个更直观的解法。先找出所有选过课的学生学号再用学生表减去这些学号最后投影姓名 πSname(Student ⋈ (πSno(Student) − πSno(SC))) 这个表达式的思路是所有学生减去选过课的等于没选课的学生。用差运算表达“排除”。查询4查询选修了全部课程的学生姓名。 πSname(Student ⋈ (πSno,Cno(SC) ÷ πCno(Course))) 除法先找到选全所有课程的学号再连接学生表取姓名。这个表达式把除法的威力完全展现出来了。6.3 一个多层嵌套的复杂查询完整走一遍查询查询选修了“数据库”和“操作系统”两门课程的学生学号。很多同学一看到“和”第一反应是AND。但这里有个坑一行SC记录只能是一门课的选课记录不可能同时出现Cname数据库 AND Cname操作系统。正确做法是先分别找到两门课的选课记录再取学号交集。设C1 πSno(σ(Cname数据库)(SC ⋈ Course))C2 πSno(σ(Cname操作系统)(SC ⋈ Course))最终答案是C1 ∩ C2。当然也可以换一种思路按学号分组统计该生选修这两门课的数量等于2 πSno(σ(Cname数据库 OR Cname操作系统)(SC ⋈ Course)) 这个要先选出两门课的所有记录再按学号分组、计数。但关系代数标准里没有专门的GROUP BY聚合运算所以用交集更贴合纯关系代数。这里想强调一个经验做关系代数题时遇到“同时满足多个条件但条件存在于不同行”的情况优先考虑连接之后的交集或除法而不是AND。AND只能处理同一行里多个属性同时满足的情况。7. 从关系代数到SQL与查询优化7.1 关系代数表达式如何“翻译”成SQL关系代数是SQL的“底层逻辑”SQL是关系代数的“实现方言”。知道它们之间的映射关系会让你学SQL时有一种“原来如此”的顿悟感。关系代数SQLσF(R)SELECT * FROM R WHERE FπA,B(R)SELECT DISTINCT A, B FROM RR ∪ SSELECT ... FROM R UNION SELECT ... FROM SR − SSELECT ... FROM R EXCEPT SELECT ... FROM SR ∩ SSELECT ... FROM R INTERSECT SELECT ... FROM SR × SSELECT * FROM R CROSS JOIN SR ⋈F SSELECT * FROM R JOIN S ON FR ⟕ SSELECT * FROM R LEFT JOIN S ON FR÷S用NOT EXISTS双重嵌套子查询举一个映射实例查询“选修了全部课程的学生姓名”SQL可以写成SELECT Sname FROM Student WHERE NOT EXISTS ( SELECT 1 FROM Course WHERE NOT EXISTS ( SELECT 1 FROM SC WHERE SC.Sno Student.Sno AND SC.Cno Course.Cno ) );外层“NOT EXISTS Course里不存在该生没选的课”就是除法的SQL形态。7.2 查询优化器靠什么“等价变换”前面反复提到“同一个查询可以有多个关系代数表达式”比如πSname(σ(SdeptCS)(Student ⋈ SC)) 和 πSname(σ(SdeptCS)(Student) ⋈ SC)逻辑上完全等价但执行代价差别很大第一个表达式先把Student和SC做全量连接再筛计算机系第二个表达式先把Student缩小成计算机系学生再和SC连接。显然第二个中间结果小得多。数据库查询优化器干的事就是在语法分析之后生成多个候选执行计划估算代价选一个最便宜的。而优化器的很多转换规则根源就是关系代数的等价变换定律。优化器最常用的启发式规则用关系代数的话讲就是选择下推把σ尽量往表达式树下方移动让选择尽早执行尽早缩小关系。投影下推把π尽量往下移动减少参与后续连接的列。连接顺序重排用交换律和结合律调整连接顺序。合并选择串接把多个选择合成一个减少扫描次数。这些规则不是考试唯一的目标但在真实的数据库性能调优里它们决定了你是写出了一个慢查询还是一个快查询。7.3 为什么我们要学关系代数三个实际理由第一它帮你写出逻辑正确的查询。很多“SQL写不出来”的情况是你没有先分析清楚查询的逻辑结构。先在纸上写一个关系代数表达式再翻译成SQL能大大降低出错率。第二它帮你理解执行计划。以后你打开数据库的执行计划看到Filter、Nested Loop、Hash Join、Projection这些名词本质上都是关系代数运算符的物理实现版本。懂了σ、π、⋈执行计划就不再是火星文。第三它帮你做SQL调优。知道选择下推能缩小中间结果你就明白为什么“先过滤再连接”比“先连接再过滤”快知道笛卡尔积的代价是指数级增长你就不会再写出没有连接条件的JOIN。8. 学习关系代数的常见误区与应试建议8.1 四个高频翻车点误区一把选择和投影混在一起。选择只筛行不筛列投影只筛列不筛行。考试里最容易出的错误是在σ后面跟一堆属性列表比如σ(Sno, SdeptCS)(Student)这是不合法的。记住σ括号里只能有逻辑条件表达式不能有属性列表。误区二忽略投影的去重语义。题目问你“选修了课程的学生有哪些”如果你的表达式是πSno(SC)没问题因为投影会自动去重。但如果你以为结果会保留每个选课记录而不去重那你就违背了“关系是集合”的底线。在SQL里SELECT DISTINCT Sno FROM SC才对应这个关系代数表达式。误区三自然连接与等值连接混淆。自然连接要求同名属性、等值比较、去掉重复列三件事同时发生等值连接只要条件成立就行结果列保留双方属性。考试如果考“R和S自然连接后有几列”记得把同名属性合并。误区四除运算的方向搞反。R÷S里S是“除数”是你要求所有X都必须覆盖的特征集合。Course表只有Cno一列是用来做除法的“除数”不是被除数。做题前先标清楚哪个是X、哪个是Y。8.2 一个可执行的练习路线我教过很多学生总结出一条比较稳的学习路径第一步把并、差、交、笛卡尔积四个运算自己造表手算一遍直到能不看书写出结果。 第二步用σ和π做二十道简单查询题每道题都写出表达式并口算验证。 第三步重点攻克连接和除把本章的例子自己完整推演三遍。 第四步挑十道历年考研/期末考试真题要求自己先写关系代数表达式再写SQL两边对照。 第五步等到学查询优化时回来重新看一遍关系代数你会发现以前记不住的等价变换定律一下子都顺了。练习时强烈建议你准备一张大草稿纸把每个表达式的执行过程画成一棵表达式树。比如R⋈S就可以画成“R和S作为叶子节点连接作为根节点”选择、投影就是树上的中间节点。画树不是浪费时间它就是在训练你用“数据流动”的视角理解查询的执行过程。8.3 从“会做题”到“会思考”考试层面关系代数的题大多是给自然语言查询写表达式或者给表达式算结果。这两个方向你都要练。前者练的是“把语义结构翻译成运算结构”后者练的是“把运算结构还原成数据结果”。到了真实工作场景你可能很少手写关系代数但你在写SQL、看执行计划、调索引的时候用的全是关系代数的直觉。那些能一眼看出两条SQL是否等价的人往往不是在背语法而是在脑内画了一棵关系代数表达式树。所以我的建议是别把关系代数当一门纯理论课去背把它当成“SQL思维的草稿纸”。它最大的价值是在你面对复杂查询时提供一个不受具体语法干扰的逻辑坐标系。你先想清楚用什么运算、按什么顺序组合再落成SQL整个查询的设计就变得非常清晰。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻