数据库系统工程师考试里,关系代数是个很特别的模块。说它简单吧,上午选择题里那些“选择不改变列数”“投影会去重”的判断,每年都能放倒一批人;说它难吧,等你在真题里把几个表达式对照着写一遍,又会发现它其实极其规整。我当年备考时,上午题靠背概念,下午题最怕的就是关系代数跟SQL互相转换,后来把运算语义一个个抠明白,才真正把它变成稳定拿分项。
简单说,关系代数就是关系数据库的底层运算语言。你在SQL里写的SELECT、WHERE、JOIN,翻译到底层本质上就是一组关系运算:选取行、选取列、拼表、过滤、匹配。它解决的核心问题是——不管你底层怎么存储,我只关心“怎么查”这件事。对于正在备考数据库系统工程师的考生来说,这个模块几乎年年出现,上午综合知识稳定出2到3分,下午案例分析题里更是常客,属于典型的“投入产出比很高”的考点。这篇文章把关系代数的核心考点从头到尾拆一遍,重点放在那些容易理解错、容易丢分、需要反复练的运算上,既有原理也有可以直接照着做的解题套路,适合正在刷真题的考生,也适合刚学数据库原理、想打牢基础的同学。
1. 关系代数在考试中的位置:先搞清楚它考什么
1.1 从真题里看关系代数的出题形态
数据库系统工程师考试分上午和下午。上午的综合知识题里,关系代数主要考概念辨析和简单计算,常见的有这几类:给你一个关系代数表达式,让你说出它执行完的结果有几个元组、几个属性;或者给你一段查询描述,问你哪个表达式能正确表示;再或者拿选择、投影、连接这几个运算摆在一起,让你判断哪个说法是错的。
下午的案例分析题就实在多了,经常给一个数据库场景,比如学生选课、图书借阅、订单管理,然后要求你用关系代数写出某个查询。这里有个细节值得注意:下午题很少直接让你写一个孤零零的关系代数表达式,更多是“这段业务需求能不能用关系代数表达,如果能,请给出表达式”,又或者“这个关系代数表达式对应的SQL是什么”。所以备考时必须两条腿走路——既会写表达式,也会把表达式翻译成SQL,还得能反向操作。
另外提醒一点,我在刷题时发现历年真题里有一个重复出现的考点:关系代数表达式与等价改写。例如给你一个自然连接的表达式,让你判断以下哪个选项和它等价。这种题表面考“等价”,实际考的是你对运算定义掌握得是否精确,后面我会专门展开。
1.2 基本概念先扫雷:关系、元组、属性、键
关系代数建立在关系模型之上,这些名词必须先过一遍,不然后面全是空中楼阁。
一个关系就是一张二维表,在关系代数里通常用R(A1, A2, ..., An)表示,R是关系名,A1到An是属性名。表中的一行叫一个元组,可以理解成一条记录;一列叫一个属性,对应一个字段。一个关系里,每一行都是唯一的,每一列都有固定的取值范围,这个取值范围叫域。比如“性别”属性,域就是{男,女};“年龄”属性的域一般就是0到150的整数。
关于键,考试里常出现四个术语:候选键、主键、外键、超键。候选键是能唯一标识一个元组的最小属性集合,主键就是你从候选键里挑出来的那个,外键是关联其他关系的属性,超键是只要能唯一标识元组就行、不一定最小的属性集合。关系代数里会频繁用到这些概念,尤其是做自然连接和除运算时,你得能快速判断出哪些属性是公共属性,哪些属性是唯一标识。
还有两个概念容易混:关系的“目”和“基数”。关系的目指属性的个数,也就是列数;基数指元组的个数,也就是行数。考试计算题经常会问“笛卡尔积之后的结果有几个属性和几个元组”,如果你把“目”和“基数”记反了,那整个计算就全错了。我给个记忆方法:眼睛是横着长的、一条一条对齐的,所以“目”对应列,一张表格有多少“列”一目了然;基数就是我们常说的“基本数量”,对应的自然是行数。
2. 五大基本操作:选择、投影、并、差、笛卡尔积
2.1 选择与投影:最容易混淆的一对双胞胎
选择用希腊字母σ表示,运算符写作σ条件(R),含义是从R中选出满足条件的行。它的作用方向是水平的,只动行不动列,结果的关系模式和原关系一模一样,属性个数不变。比如查询计算机系的学生,写成σ系别='计算机'(学生),结果仍然有学号、姓名、系别、年龄这些列,只是行变少了。
投影用π表示,写法是π属性列表(R),含义是从R中选出指定的若干列。它的作用方向是垂直的,只动列不动行,结果的关系模式由你选择的属性组成。比如查询所有学生的姓名和系别,写法是π姓名,系别(学生)。
这里有个特别重要的考点:投影会去重。正因为关系是集合,集合中不允许有重复元素,所以投影出来的结果如果有多行相同,只保留一行。我见过太多考生在这个点上丢分了。举个典型例子:查询所有学生的所在系,学生表里可能有十个学生来自“计算机系”,投影π系别(学生)之后结果就只有一行“计算机系”,不是十行。这个性质在考题里经常以“该表达式的结果有几行”的形式出现,你不仅要会算,还要在往SQL转换时意识到“SELECT DISTINCT”的对应关系。
选择的条件表达式里可以出现比较运算符和逻辑运算符,逻辑关系常用∧表示且、∨表示或、¬表示非。比如查询计算机系并且年龄大于20的学生,写成σ系别='计算机'∧年龄>20(学生)。要注意逻辑运算符的优先级和括号使用,考试中宁可多加括号也别省略,表达式一旦有歧义,改卷时很容易被判定为不完整。
2.2 并、差、笛卡尔积:集合运算的考场规矩
并运算R∪S,取两个关系所有元组的并集。差运算R-S,取属于R但不属于S的元组。这两个运算有一个前提条件叫“相容性”,也叫“并相容”:两个关系的属性个数必须相同,并且对应属性的域也相同。举个例子,你不可能对一张“学生(学号,姓名)”的表和一张“选课(学号,课程号)”的表做并运算,因为属性列表对不上。考试时判断“以下哪些关系之间可以进行并、差运算”,核心就看这个。
还有一个容易忽略的细节:并和差的结果也会自动去重。关系是集合,集合里不会有两条一模一样的元组。所以当你把两个表UNION起来时,重复的记录自动消失,这跟SQL里的UNION语义完全一致。
笛卡尔积用×表示,R×S的结果是一个新的关系,它的属性数是两个关系属性数之和,元组数是两个关系元组数之积。这个计算本身不难,但考试喜欢把它放在复合题里。比如R有3个属性和5行,S有4个属性和6行,R×S的结果就是7个属性30行。如果题目再往下问“对笛卡尔积做选择之后剩下几行”,你就得会结合条件去数。
需要特别注意的是,当R和S有同名属性时,笛卡尔积的结果里会同时出现R.A和S.A两列。在关系代数里通常用属性名前缀来区分,比如R.A和S.A。这种命名方式在后面写连接条件时会频繁用到,也是关系代数表达式转SQL时容易出问题的地方。
2.3 更名运算:一个容易被忽略的得分点
更名运算用ρ表示,写法是ρ新名字(R),看名字就明白,它给关系重新取个名字。这个东西表面简单,但它是解决自连接问题唯一的手段。什么叫自连接?就是一个关系和自己做连接。比如查询“年龄比刘芳大的学生姓名”,你需要把学生表和它自己比较,如果不改名,两个都是“学生”关系,在表达式里根本无法区分哪个是“刘芳所在的行”,哪个是“其他学生所在的行”。
写成ρS1(学生),ρS2(学生),然后用S1、S2去做笛卡尔积、选择、投影,表达式就清晰了。完整写法大致是:
πS1.姓名(σS1.年龄>S2.年龄 ∧ S2.姓名='刘芳'(ρS1(学生) × ρS2(学生)))
这个考点即使在选择题里也经常出现,判断依据很简单:看到一张表和它自己比较,第一反应就是需要更名,没有更名运算的关系代数表达式,十有八九是错的。
2.4 复合表达式与运算优先级:别让括号背锅
一个关系代数的查询,通常不是单独一个运算,而是多个运算套在一起。比如“查询计算机系学生的姓名”,必须先做选择,再做投影,写成π姓名(σ系别='计算机'(学生))。
运算顺序在关系代数里是有优先级的,一般来说,选择、投影这类一元运算优先级最高,笛卡尔积、连接次之,并、交、差最低。但我的建议是:考试时永远不要依赖优先级记忆,直接加括号。加括号不是为了给老师看,而是防止你自己把运算顺序搞反。我在复查真题时发现,很多考生写表达式不是不会,而是括号位置错了一层,结果整个语义完全变了。
另外有个实用技巧:能先做选择就先做选择,能先做投影就先做投影。这不是考试要求,而是从查询优化角度考虑的习惯——先把关系变小,再做笛卡尔积或连接,中间结果集就小得多。这个习惯考试不扣分,但对你理解“查询优化”这个完整考点有直接帮助,上午题偶尔也会以“以下哪个等价改写更优”的形式出现。
3. 连接操作:自然连接是隐藏扣分重灾区
3.1 自然连接:先等值匹配再合并列
自然连接用⋈表示,是考试里出现频率最高的连接方式,也是出错频率最高的。它的执行分两步:第一步,找出两个关系中的同名列,在这些列上做等值比较;第二步,把结果中的重复列合并成一列。
举个例子。关系R的属性是(A, B, C),关系S的属性是(B, C, D),两个关系的公共属性是B和C。自然连接R⋈S会先比较R.B与S.B是否相等、R.C与S.C是否相等,然后生成一个新关系,属性是(A, B, C, D),B和C只保留一份。
这个合并列的细节是考试最爱挖的坑。等值连接的结果不会自动合并重复列,而自然连接会,很多考生把两者混为一谈,一看到“自然连接”就用等值连接的思路去做,结果属性个数算错。我建议你把这句话记下来:自然连接 = 笛卡尔积 + 公共属性等值条件 + 合并重复列。
完整的等价表达式可以写成这样:
自然连接 R⋈S 等价于 πR.A, R.B, R.C, S.D(σR.B=S.B ∧ R.C=S.C(R × S))
这也是上午题等价改写题的标准解法,看到“以下哪个表达式等价于R⋈S”,直接按这个模板去比对选项。
3.2 等值连接与θ连接:不合并列的连接
等值连接和θ连接在考试里通常一起出现。θ连接是更一般的连接方式,θ可以是任意比较运算符,比如大于、小于、不等于。当θ取“=”时,就是等值连接。写法上,等值连接一般写成R⋈R.A=S.AS,θ连接写成R⋈R.A>S.AS。
等值连接和自然连接的区别必须掰清楚:第一,等值连接需要你明确指定连接条件,自然连接不需要、它自动找同名列;第二,等值连接的结果会把R.A和S.A作为两列都保留下来,而自然连接会合并。举个具体例子,R(A,B)和S(B,C),自然连接的结果是(A,B,C),而按R.B=S.B做等值连接的结果是(A, R.B, S.B, C),B会出现两次。
考试判断技巧:题目里如果明确写了连接条件是“R.属性 = S.属性”,那就是等值连接,不要画蛇添足去合并列;如果只写了“自然连接”四个字,才需要考虑同名列合并。还有一类题会问“下列哪种连接不会产生重复属性”,答案就是自然连接。
3.3 外连接:把悬浮元组找回来
自然连接和等值连接有一个共同毛病:如果某一行在另一个关系里找不到匹配,这一行就会从结果中消失,这种被丢弃的行叫悬浮元组。比如学生表里有三个学生,选课表里只有一个学生选了课,自然连接之后结果里只会出现那一个有选课记录的学生,另外两个学生虽然人还在,但在连接结果里看不到。
外连接就是为了解决这个问题。左外连接会保留左边关系的所有元组,右边找不到匹配就填空值;右外连接保留右边关系的所有元组;全外连接则两边都保留。这个考点在上午题里经常以概念判断出现,在下午题里则可能让你写“查询没选课的学生”——这时候你需要先做左外连接,再做选择,选出右边属性为空的那些行。
我建议把外连接的三种形式和自然连接的等价表达式一起记,因为选择题里会拿这些做干扰项。区别核心就一句话:是否保留、保留哪边的悬浮元组。
3.4 连接之后的元组数与属性数计算
这是计算题最常见的形态,我给你一套完整方法。先算属性数:自然连接的结果属性数等于两个关系属性数之和减去公共属性个数;等值连接不合并,属性数直接是两关系属性数之和。再算元组数:这个没有固定公式,必须先做笛卡尔积,再数满足连接条件的行数。
拿一个简单例子推一遍。R有属性(A,B),两条记录:(1,2)、(3,4)。S有属性(B,C),两条记录:(2,5)、(2,6)。自然连接时,公共属性是B,R中B=2的行和S中B=2的两行都能匹配,所以结果是两行:(1,2,5)和(1,2,6),属性数是3,元组数是2。如果你算出来的元组数等于两个关系行数相乘再乘以某个比例,那不是规律,只是这题碰巧如此。考试中的正确做法永远是一个个对照公共属性值去数,别套什么“行数相乘再除以公共属性数”之类的野公式。
4. 除运算:压轴难点,理解了就是送分题
4.1 除运算到底在算什么
除运算用÷表示,是所有关系代数运算里最抽象、也最像“压轴题”的一个。它的标准语义是这样的:对于两个关系R(X,Y)和S(Y),这里X和Y都是属性组,R÷S的结果是那些X值满足“在R中出现的所有Y值,覆盖S中的所有Y值”的元组。
听起来很绕,换成大白话就是:S里要求什么,R里的X就必须全部满足。这也是为什么考试时遇到“查询选修了全部课程的学生”“查询供应了所有零件的供应商”这类带“所有”“全部”字样的题目,十个里有九个要用除运算。
举个例子。选课关系R(学号, 课程号),课程关系S(课程号)。假设R里有三个学生:学号001选了C1、C2、C3,学号002只选了C1,学号003选了C1、C2。S里有C1、C2、C3三门课。R÷S的结果是哪个学生?只看课程号完全覆盖了S中所有课程的学生,那就是001。002缺C2、C3,003缺C3,都不满足。这个例子的结果最终是只含学号001的一个关系,属性只有“学号”,因为除运算的结果属性是X,也就是R中除了Y之外的属性。
4.2 除运算的通用解题套路
考试时除运算不要硬想“语义”,直接用三步法操作,又快又不会漏:
第一步,确定X和Y。X是R的属性中去掉S属性后剩下的属性,Y就是S的全部属性。这里有个前提,S的属性必须都包含在R中,否则除运算无意义。
第二步,把R按X的值分组。比如按学号分组,每个学号对应一个课程号的集合。
第三步,看每组课程号集合是否包含S中所有课程号。包含的组留下来,最后输出这个组的X值。
这个方法对所有除运算都有效。不过光会这个还不够,上午题偶尔会考除运算的等价表达式。标准答案是:
R÷S = πX(R) - πX(πX(R) × S - R)
这个公式看着复杂,拆开看其实不复杂。πX(R)先找出所有出现过的X值,πX(R) × S是X与S的全部可能组合,减去R就找出了那些“该有却没有”的组合,再投影X,最后用全集减去这个集合,剩下的就是完全覆盖S的X值。选择题里如果出现类似“哪个表达式等价于R÷S”,直接按这个结构找。
4.3 一个完整的例题拆解
我用一个经典例子把除运算从头到尾走一遍。关系R(A, B),这里A类比学生,B类比课程,具体数据为:(A1, B1)、(A1, B2)、(A2, B1)、(A3, B1)、(A3, B2)、(A4, B2)、(A4, B3)。关系S(B),只有一个属性B,数据为B1、B2。
求R÷S。按三步走:X是A,Y是B。按A分组,A1对应{B1,B2},A2对应{B1},A3对应{B1,B2},A4对应{B2,B3}。S要求的是{B1,B2},所以A1、A3满足,A2缺B2,A4缺B1。最终结果是两个元组:A1和A3。
如果你非要用SQL去验证,对应的SQL是经典的NOT EXISTS写法:
SELECT DISTINCT A FROM R AS R1 WHERE NOT EXISTS ( SELECT 1 FROM S WHERE NOT EXISTS ( SELECT 1 FROM R AS R2 WHERE R1.A = R2.A AND R2.B = S.B ) )
考试时不会要求你写这么复杂的SQL,但这个对应关系能帮你验证思考方向是否正确。我做题时养成了一个习惯:关系代数算完,如果题目有时间,就用SQL的逻辑在心里反推一遍,两边对得上才敢写进答案,因为关系代数写错一步,阅卷时很容易一眼看出,而SQL验证能帮你把错误提前拦住。
5. 关系代数与SQL互转:下午题的提分关键
5.1 转换对应规则表
关系代数转SQL,考的是“你能不能看懂表达式背后的查询逻辑”。这里先给一张我总结的对应关系表,后面所有示例都围绕它展开。
| 关系代数 | SQL对应 |
|---|---|
| σ条件(R) | WHERE 条件 |
| π属性列表(R) | SELECT 属性列表 |
| R ∪ S | SELECT ... FROM ... UNION SELECT ... |
| R - S | SELECT ... FROM ... EXCEPT SELECT ... 或 NOT IN |
| R × S | FROM R, S / CROSS JOIN |
| 自然连接 | JOIN ... USING(公共属性) |
| 等值连接 | JOIN ... ON R.A = S.A |
| 除运算 | NOT EXISTS 嵌套查询 |
| 更名ρ | 表的别名 |
这个表看起来简单,但有两个坑必须提醒。第一,投影π在SQL里对应SELECT,但关系代数的投影自动去重,SQL的SELECT默认不去重,只有加DISTINCT才去重。如果你转换时想精确保持语义,记得在SELECT后面加DISTINCT。第二,关系代数的差运算对应SQL的EXCEPT,但很多考试环境下不允许用EXCEPT,这时候要用NOT IN或NOT EXISTS来改写,这两种写法都要会,因为下午题答案不唯一,批改时看你表达的逻辑是否等值。
5.2 带条件的投影和排序:转换时的顺序问题
转换过程中最容易出错的不是单个运算,而是运算顺序。关系代数的写法是内层先执行,外层后执行,比如π姓名(σ年龄>20(学生)),先筛选年龄大于20的行,再投影姓名。SQL里最直观的写法是:
SELECT 姓名 FROM 学生 WHERE 年龄 > 20
你发现没有,SQL把投影写在了前面、条件写在后面,但执行顺序是先WHERE再SELECT。这里有个很实用的转换技巧:写SQL时先看关系代数的内层运算,把它翻译成FROM和WHERE部分,再看外层运算,把它翻译成SELECT部分。从里往外一层层剥,基本不会乱。
如果关系代数里既有选择和投影,又涉及连接,顺序就更重要了。比如πS.姓名(σS.年龄>20(学生S ⋈ 选课)),它表示“年龄大于20岁并且有选课记录的学生姓名”。SQL写出来是:
SELECT DISTINCT S.姓名 FROM 学生 S JOIN 选课 C ON S.学号 = C.学号 WHERE S.年龄 > 20
注意我在SQL里使用了别名S,这正好对应关系代数里的更名运算。把关系代数的名字对应到SQL别名,是一个很好用的转换锚点,能帮你快速定位哪个表是哪个角色。
5.3 经典转换示例:选修了所有课程的学生
这个例子几乎每年都有学校考、每年都有考生错,我完整写一遍。用关系代数表示“查询选修了全部课程的学生学号”,最标准的写法是:
π学号(选课 ÷ π课程号(课程))
翻译一下:课程表投影出所有课程号,再用选课关系除以这些课程号,剩下的就是选修了全部课程的学生学号。转成SQL有两类写法。
第一类用NOT EXISTS,逻辑最贴近除运算:
SELECT DISTINCT 学号 FROM 选课 A WHERE NOT EXISTS ( SELECT 1 FROM 课程 WHERE NOT EXISTS ( SELECT 1 FROM 选课 B WHERE A.学号 = B.学号 AND B.课程号 = 课程.课程号 ) )
第二类用COUNT和分组,思路是“该学生选课数 = 总课程数”:
SELECT 学号 FROM 选课 GROUP BY 学号 HAVING COUNT(DISTINCT 课程号) = (SELECT COUNT(*) FROM 课程)
两类写法都没问题。实际备考我建议你两种都练,因为上午题可能会考“以下哪个SQL语句等价于给定的关系代数表达式”,选项中可能既有NOT EXISTS也有GROUP BY写法,你两种都看得懂才能稳拿这分。
5.4 反向转换:SQL转关系代数的三个注意点
反方向转换,也就是从SQL写关系代数,同样要注意三点。
第一,SELECT后面的属性列表直接写成投影π,但如果SQL里用了DISTINCT,投影本来就会去重,所以关系代数里不用额外处理。如果SQL里没有DISTINCT,理论上关系代数也应该去掉重复,但在考试环境下,只要查询语义一致,一般都会接受。
第二,FROM后出现多个表名,对应的是笛卡尔积或者各种连接。如果SQL里是“FROM R, S WHERE R.A = S.A”,转换时可以先写选择再写笛卡尔积,也可以直接写等值连接,推荐后者更简洁,写成R⋈R.A=S.AS。
第三,子查询是最难转换的部分。SQL里出现在WHERE后面的IN子查询,转换到关系代数时往往需要用到差运算或除运算。比如“查询没选课程1的学生学号”,SQL是“SELECT 学号 FROM 学生 WHERE 学号 NOT IN (SELECT 学号 FROM 选课 WHERE 课程号='C1')”,关系代数写法就是:
π学号(学生) - π学号(σ课程号='C1'(选课))
这个对应关系记熟了,下午题再遇到“请用关系代数表示”就不容易懵。
6. 高频错题与考前自查清单
6.1 高频丢分点速查表
我调研了大量历年真题和模拟题,把关系代数最常见的丢分点整理成一张表,备考最后阶段可以直接对照自查。
| 丢分点 | 典型错误 | 正确认知 |
|---|---|---|
| 选择与投影混淆 | 把σ写成删列,把π写成删行 | σ只选行,π只选列 |
| 投影去重 | 投影后仍按原行数计算 | 结果中重复行只保留一条 |
| 自然连接合并列 | 等值连接与自然连接混用 | 自然连接自动合并同名列 |
| 连接条件未加前缀 | R和S有同名属性时不加表别名 | 同名属性必须用R.A/S.A区分 |
| 除运算属性范围 | 结果包含了Y属性 | 结果只含X属性 |
| 并差运算相容性 | 对属性个数不同的关系做并差 | 必须先判断相容性 |
| 关系代数转SQL去重 | SELECT后忘加DISTINCT | 投影对应SELECT DISTINCT |
| SQL转关系代数子查询 | NOT IN转成差运算不熟练 | NOT IN对应差运算或NOT EXISTS |
这张表我建议你贴在手边,每次刷题前扫两遍。我自己的习惯是,做完题对答案时先不急着看解析,而是先在表里定位自己错的是哪一类,再回头排列组合地重做一遍,效果比盲目刷十套题还好。
6.2 考场上怎么快速验证表达式正确性
考试时间紧张,关系代数表达式写完没有“跑一遍”的条件,但有三个快速自检方法,都是我在刷真题过程中总结出来的。
第一个,数属性。写完表达式后,按运算顺序重新数一遍结果应该有几个属性。如果是自然连接,属性数是两边之和减公共属性数;如果做了投影,属性数就是你写出的属性列表长度。这一步能拦住大半粗心错误。
第二个,检查条件的属性前缀。只要表达式中出现了笛卡尔积或者连接,选择条件里的每个属性都要确定归属,尤其是同名属性,必须写R.属性或S.属性。考试里经常出现不写前缀的表达式,一眼看过去很规范,实际上一旦有同名属性,这个表达式就是无效的。
第三个,用极端数据做心理推演。随便假设一个只含两条记录的小关系,在脑子里走一遍运算过程,看结果是否符合常识。这个方法听起来笨,但特别管用,因为关系代数很多错误不是概念问题,而是“做的时候根本没意识到运算顺序想岔了”。一个只有两行数据的小例子,走一遍就能暴露问题。
6.3 时间分配与刷题顺序建议
如果你现在距离考试还有一个月左右,我建议把关系代数复习分三个阶段推进。前十天做基础练习,每天固定十几道选择加两个表达式的书写,重点吃透选择、投影、连接的语义区别,把上面那张速查表上的错误挨个“踩”一遍再纠正。中间十天做真题专项,把所有历年真题中的关系代数题集中起来,掐时间做,做完重点分析下午题的关系代数转SQL,因为这部分最容易暴露出你对运算顺序理解不透的问题。最后十天做综合模拟,每天做一套完整卷,把关系代数放在下午题的时间预算里统一规划。
关于时间分配,我给一个大致参考:一道下午案例分析题如果包含关系代数,建议用时控制在十分钟以内。前五分钟写出表达式,后五分钟做自检和SQL对照。不要在一个表达式上反复纠结,宁可先写一版,再通过自检修正,也比卡在那里浪费后面大题的时间强。
结尾
最后说点备考之外的体会。关系代数这个东西,光背定义真的没用,它考的是“能不能把一句业务查询用精确的运算步骤表达出来”。我当年复习时给自己定了一个规矩:每做一道SQL大题,就强迫自己用关系代数再写一遍;反过来,看到关系代数表达式,也先在SQL里还原出来。坚持了两周之后,再回头看上午题那些概念判断题,完全没有模糊空间,因为你在两种语言之间来回切换的过程中,已经把每个运算的边界摸清了。这个习惯直到现在做需求时还在用——写SQL前先在草稿本上画一画关系运算,很多慢查询和错误关联当场就能发现。希望这篇整理能帮你把这块硬骨头啃下来,上午题拿稳那几分,下午题不再被关系代数绊住。