拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

软考数据库系统工程师:关系代数核心考点与解题全攻略

软考数据库系统工程师:关系代数核心考点与解题全攻略

距离软考数据库系统工程师考试还有一段时间的时候,总有人问我:关系代数到底怎么学?教材翻来翻去就那几页,选择、投影、连接、除运算看起来也不复杂,可一到真题就懵,尤其是那些带“全部”“至少”“没有”字眼的查询题,完全不知道从哪里下手。我备考时也踩过不少坑,后来帮别人答疑,慢慢总结出一套比较靠谱的拆解方法。这篇就把关系代数的核心考点掰开揉碎讲一遍,重点讲清楚那些让你丢分的地方,并给出一套可以直接用的解题流程,从基础概念到除运算,再到自然语言翻译成表达式的实战套路,一次性理清。

1. 为什么关系代数看着容易,做题却总在细节上翻车

1.1 关系代数在软考里的三种常见考法

软考数据库系统工程师对关系代数的考查,上午题和下午题各有侧重。上午是单选题,喜欢考“结果长什么样”:给两个关系R和S,告诉你各自的属性个数和元组数,然后问笛卡尔积、自然连接、投影之后各是多少列多少行。这类题看起来是在考计算,实际上考的是你对集合操作的底层理解。下午的案例分析题则直接让你写关系代数表达式,比如“查询选修了全部课程的学生的姓名”,没有选项可猜,只能靠平时积累的表达功底。

还有一种考法是“翻译题”。给一段SQL,问哪个关系代数表达式和它等价;或者反过来,给一个关系代数表达式,问它对应什么语义。这种题每年都会出现,本质上还是考察几个基本运算是否真的理解。很多人选择、投影背得熟,一到自然连接和除运算就漏细节,往往就是在这类题上丢分。

另外,上午题还喜欢考“两个表达式是否等价”。例如σ条件作用于连接之后,是否可以先做选择再做连接;投影和选择是否可以交换顺序。如果只是死记结论,碰到具体关系很容易判断错。后面我会专门讲清楚这些等价变换的条件。

1.2 丢分最多的三个基础性错误

第一个错误:把选择条件和投影列混在一起。比如查“CS系中年龄大于20的学生的姓名”,有人写成π(sname, sdept='CS')(Student),把布尔条件塞进投影的属性列表里。投影π只负责取列,选择σ才负责过滤行,这是两个维度的操作,不能混在一个运算符里。正确写法是π(sname)(σ(sdept='CS' AND sage>20)(Student))。

第二个错误:忘了投影会去重。这是关系代数和SQL最容易起冲突的地方。关系代数的关系是一个集合,集合里不可能有重复元组,所以π(sno)(SC)的结果中,每个学生只出现一次,哪怕他在SC里有十条选课记录。但SQL里SELECT sno FROM SC默认输出的是多集,会有重复行。很多上午题就专门在这里挖坑,问投影结果有几个元组,你用SQL的习惯去数,必错。

第三个错误:把等值连接和自然连接当成一回事。等值连接的结果包含两个关系的所有列,如果有同名列,会出现两列同名;自然连接则把同名属性合并为一列。这个差别直接影响了结果列数,也影响后续操作的属性引用。很多综合题里,明明应该用自然连接的地方先用等值连接,最后投影时就找不到那个同名列了。

2. 五大基本运算逐个拆解:选择、投影、笛卡尔积、并、差

2.1 选择σ:筛选行,结果模式不变

选择的符号是σ,右下角写条件,括号里放关系。比如σ(sage>20)(Student),就是从Student表里取出年龄大于20的全部行。选择操作的结果模式和原关系一模一样,属性个数不变,只是元组被过滤掉一部分。条件内部可以用∧表示“且”,∨表示“或”,¬表示“非”,这些逻辑连接词和编程语言里的AND、OR、NOT是一个意思。

选择里最容易犯的错是条件中写了不存在的属性名。比如给的是选课表SC(sno, cno, grade),你写σ(credit>3)(SC),但credit在Course表里,不在SC表里,这个表达式就是非法的。另外在涉及多表连接后,如果两个表都有相同属性名,引用时一定要加上关系名前缀,比如Student.sno = SC.sno,否则会造成歧义,考试题里经常把这个作为判断对错的关键点。

选择还有一个特性:可以进行条件合并。σF(σG(R))等价于σ(F AND G)(R),这个知识点虽然简单,但上午题偶尔会考,而且它也是后面查询优化“选择合并”的基础。在写复杂表达式时,我习惯把多个条件先合到一起,减少运算步骤,最后再考虑能否下推到某个表上。

2.2 投影π:选列,顺便给你去重

投影的符号是π,右下角写属性列表,括号里是关系。比如π(sname, sno)(Student),结果只保留sname和sno两列。投影结果里如果出现了完全相同的元组,会自动合并成一条。这个去重是集合语义自带的,不需要额外写什么DISTINCT。于是考试里经常出现这种题:SC表里有一个学生选了5门课,问π(sno)(SC)有几行?答案是1行,不是5行。

投影另一个考点是属性顺序。π(sname, sno)(Student)结果的属性顺序是sname在前,sno在后,和原表属性顺序无关。有的题目会让你写出结果模式,如果你按原表顺序写,就会丢分。还有一点,投影后如果属性列表里包含了两个同名的属性,需要先通过重命名区分,否则无法表达。

投影和选择经常组合使用。口诀是:先选择缩小行数,再投影取列。虽然理论上先投影后选择也可以,但受属性限制,如果选择条件用到的属性没有包含在投影列表中,就必须先选择再投影。所以标准动作是先σ后π,这也是后面讲“选择下推”时的基本套路。

2.3 笛卡尔积×:一切连接的基础

笛卡尔积的符号是×。R×S的结果是R的每个元组和S的每个元组做全组合。假设R有m个属性、p个元组,S有n个属性、q个元组,那么R×S有m+n个属性、p*q个元组。上午题特别喜欢考这个计算,公式很简单,但要注意别把属性和元组弄反。

实际查询中很少直接使用笛卡尔积,因为中间结果太大,绝大多数场景最终会被选择条件过滤掉。考试里也经常用“必须先做笛卡尔积再选择”来讲述连接的由来,所以不难看到题目先考你R×S的结果列数和行数,再让你结合选择条件算出最终结果。

笛卡尔积有一个隐藏坑:如果R和S中有同名属性,结果里会出现两个同名列,引用时必须写成R.A、S.A。这样做的结果就是无法直接用属性名区分。解决它的办法是重命名,或者直接使用自然连接。因此在写表达式时,如果两个表有共同属性,我通常会用连接而不是笛卡尔积加选择,既是语义更清晰,也能减少列数。

2.4 并∪、差−与相容性:集合运算先查户口

并、差、交三种集合运算要求两个关系必须相容。相容的意思不是表名相同,而是属性个数相同,并且对应属性取自同一个域。比如R(A, B)和S(A, C),虽然属性个数都是2,但B和C域不同,就不能直接做并运算。考试里常给一张学生表、一张教师表,问这两个关系能不能做并,答案是不能,因为属性结构不一致。

并运算R∪S的结果是把R和S的元组放到一起再去重。计算元组数时,不能简单相加,要先看两个集合有多少重复元组。差运算R-S的结果是“在R中出现但不在S中出现的所有元组”。方向很重要,R-S跟S-R一般完全不同。交运算R∩S表示同时在R和S里出现的元组,它可以用基本运算推导出来:R∩S = R-(R-S)。这个推导式上午题偶尔会让判断,可以在草稿纸上画两个圆理解,非常直观。

这三种运算在中文查询题里对应“或”“非”“且”,比如“选了数据库或选了操作系统的学生”用并,“没选任何课程的学生”用差,“既选了C1又选了C2的学生”用交。看到“没有”“从未”这类否定词,第一反应就是差运算。

2.5 重命名ρ:自连接里的无名英雄

重命名运算符是ρ。ρ(S, A1, A2, ...)(R)可以把关系R改名为S,同时把属性也改成新名字。很多初学者会忽略它,但考试中大量“自己跟自己比”的查询都要靠它。比如要查“课程表中哪些课程的先修课是它自己”,或者“至少选修了课程C1和课程C2两门课的学生”,如果不给同一张表重命名,就没办法同时引用它的两个不同实例。

我说一个记忆技巧:自连接的本质是复制一份一模一样的表,然后让两个副本进行比较。为了不让属性名冲突,必须给其中一个副本改名。重命名之后,引用属性要带上新的关系名,比如SC1.sno、SC2.sno。上午题如果给你一个表达式,里面出现了SC1和SC2,你就要能立刻反应过来它是在做自连接,通常对应的是“至少”“不同于”这类含义。

重命名还有一个用途:让两个关系拥有同名属性,从而触发自然连接。比如两个表Semester(sno, cname)和Schedule(sno, cname),本来属性名可能不同,但通过重命名把公共属性统一,自然连接就不需要手写连接条件了。这个技巧在下午题书写时能省不少事。

3. 连接运算三兄弟:θ连接、等值连接、自然连接

3.1 为什么自然连接不是等值连接的简写

连接运算看似简单,其实有三个容易混淆的变体。θ连接是最一般的形式,表达式是R⋈(F)S,其中F是任意连接条件,结果包含R和S的所有列,同名列会同时保留。等值连接是θ连接的特例,连接条件只包含等号比较,比如R.B=S.B,结果同样包含两个关系的全部列。自然连接形式上不需要写连接条件,它自动找出两个关系的所有同名属性,在这些属性上做等值连接,并且把这些同名属性合并成一列。

自然连接和等值连接的关键区别就在这:自然连接多了一步“消除同名列”。举个例子,Student(sno, sname)和SC(sno, cno)做等值连接,条件是Student.sno=SC.sno,结果有4列:sno, sname, sno, cno。如果做自然连接,结果只有3列:sno, sname, cno。两个结果的行数可能一样,但模式不同。

很多同学在写表达式时不注意区分,默认就写自然连接。如果题目给出的两个关系公共属性不叫同一个名字,比如Student(sid, sname)和SC(sno, cno),那么它们根本找不到同名属性,自然连接会退化成笛卡尔积。所以写之前必须先确认公共属性是不是同名。如果不同名,要么先重命名,要么用θ连接加明确条件。

3.2 用一个例子算清列数变化

假设Student表有5个属性:sno, sname, ssex, sage, sdept,SC表有3个属性:sno, cno, grade。两表只有sno同名。这时:

  • Student×SC:8列。
  • 等值连接(Student.sno=SC.sno):8列,sno会出现两次。
  • 自然连接:7列,sno只保留一次,后面接sname, ssex, sage, sdept, cno, grade。

上面这个“5+3-1=7”的算法在上午题里是高频考点。如果两个关系有多个同名属性,自然连接结果列数就是两个属性个数之和减去同名属性个数。比如R(A,B,C)和S(B,C,D)自然连接后是A,B,C,D,不是6列,而是4列。这类题基本不需要算,记住公式就行。

还有一个延伸:如果两个关系没有任何同名属性,自然连接结果就是笛卡尔积。这听起来反直觉,但确实是自然连接的定义——在空集属性集合上做自然连接,找不到等值条件,只能把所有元组配对。题目如果问“两个关系自然连接会不会没有结果”,答案不是空集,而是回到全配对。

3.3 悬浮元组与连接语义的取舍

自然连接是内连接,只有两边都匹配上的元组才会出现在结果里。假如SC表里有一个学生学号在Student表中不存在,自然连接后这个学生的所有选课记录都会消失。这在语义上是合理的,因为你查“学生选课信息”时,查不到那些没有学生主信息的记录。但在实际数据处理中,这种“悬浮元组”被丢弃往往会造成统计偏差。

考试里也出现过类似的题:先求自然连接,再对结果做统计,问某些学号的记录为什么少了。正确思路是先意识到自然连接做了内连接过滤,而不是怀疑数据出错。如果你想保留那些不匹配的记录,关系代数里需要外连接,但外连接不是软考关系代数部分的重点,最多作为扩充概念提一下,掌握到能判断“什么时候该用自然连接、什么时候会被过滤”就够了。

连接操作的执行顺序也值得注意。多表连接时,先把哪两张表连起来效率差别很大。理论上关系代数不关心执行顺序,但考试中的查询优化题喜欢让你判断:σ(cno='C1')(SC)和Student自然连接,比先Student和SC连接再做选择,哪个中间结果小?答案显然是先对SC做选择。这就是“先选择,后连接”的优化原则。

4. 除运算凭什么最难:从定义到“候选值-失败组合”法

4.1 除运算的直观意义

如果让我选关系代数里最抽象的一个运算,我会选除运算。它的符号是÷,表达式是R÷S。使用条件是S的非空属性集合是R属性集合的子集。结果关系的模式由R中那些不在S里的属性组成。听起来绕,通俗讲就是:找出R中哪些“实体”和S中所有的元组都产生过对应组合。

举最经典的例子:SC(sno, cno)除以Course(cno),得到的就是选修了全部课程的学生学号。为什么?因为SC包含了“学生-课程”的对应关系,Course包含了所有课程,除法的意思就是:对每个学生,检查他是否和Course里的每门课程都形成过选课记录。如果全部存在,这个学生就出现在结果里。

再举一个更简单的例子。R(sno, cno)有数据:a选了c1、c2、c3,b只选了c1,c只选了c2。S(cno)是{c1, c2}。R÷S的结果应该只有一个学号:a。因为a同时有c1和c2两条记录,b没有c2,c没有c1。这个例子很适合手算,你可以把R看成一个小矩阵,横向是学号,纵向是课程,除运算就是看哪些行能覆盖S中所有课程。

4.2 用基本运算推导除运算公式

除运算虽然看起来高深,但它可以用基本运算表达出来,这个推导式也是理解除法的钥匙:

R÷S = πX(R) − πX((πX(R)×S) − R)

其中X是R的属性减去S的属性后剩下的属性集合。以R(sno, cno)、S(cno)为例,X就是{sno}。公式分四步理解:

  1. πX(R)取出所有候选学号,比如{a, b, c}。
  2. πX(R)×S把每个候选学号和S中的每个课程全组合,生成完整的“候选学生-课程”配对表。
  3. 这个完整配对表减去R,找出哪些配对在真实选课表里不存在。例如b和c2这个配对,在R里没有,所以它会出现在这里。
  4. 把这些“失败组合”投影到X,得到有失败记录的候选学号;用全部候选学号减去这些失败学号,剩下的就是每一步都配对成功的学号。

你可以把“候选值-失败组合”当心算法。每道除法题,先列出候选值,再看每个候选值缺不缺S中的某些元组,只要缺一个就淘汰。这个心算法比公式更直观,也不容易出错。考试中你不需要默写公式,但理解公式能帮你解释为什么某些表达式是错的。

4.3 除运算典型真题拆解

来看一道高频下午题:已知Student(sno, sname, ...)、Course(cno, cname, ...)、SC(sno, cno, grade),查询“选修了全部课程的学生姓名”。很多人的第一反应是拿SC整体去除以Course整体,但这会出问题。Course的属性有cno、cname、cpno、credit,而SC里只有cno和sno、grade,cname不在SC中,除法使用条件不成立。

正确的做法是先把两个关系投影成干净的形式:

T1 = π(sno, cno)(SC),T2 = π(cno)(Course),然后计算 T1÷T2,得到选修全部课程的学号集合T3。接着再和Student自然连接:T4 = Student ⋈ T3,最后投影sname:π(sname)(T4)。

完整表达式是:π(sname)(Student ⋈ (π(sno, cno)(SC) ÷ π(cno)(Course)))。两个易错点都在投影:被除数的SC不能带grade,除数的Course不能带cname。如果带上前者,结果模式会多出grade,学生可能因为不同成绩而出现多条;如果带上后者,除法的属性集不是SC的子集,直接非法。

除运算还有一个常见变式:查询“至少选修了课程C1和课程C2的学生”。这种题不需要Course表,可以构造一个有两条元组的除数S(cno)={('C1'), ('C2')},然后SC投影后除以它。如果不想用除运算,用两个选择的交也可以,后面第5章我会专门讲这个等价写法。

5. 把中文查询题翻译成关系代数的五步流程

5.1 五步拆题法

做关系代数综合题,最忌讳的是拿起笔就写。我习惯按五步拆解:

第一步,抓“动词”。题目里如果出现“列出”“显示”“查询”,说明外层常常是投影π;出现“筛选”“满足条件”,对应选择σ;出现“和/或/否”,对应集合运算∪、−、∩;出现“全部”“每个”,大概率对应除运算。动词决定了表达式的骨架,一上来就能排除一半错误。

第二步,圈“名词”。把题目涉及的表全部圈出来,看查询结果需要哪些属性,这些属性分布在哪些表里。比如“学生姓名”在Student,“课程编号”在Course或SC。如果结果属性和条件属性不在同一张表,那就必须通过连接把它们串起来。

第三步,找量词。这是最容易丢分的地方。“至少”“全部”“所有”“没有”“从未”都是信号词。“全部”对应除,“至少”可能是除或交,“没有”基本是差。把信号词和运算符对应好,等于把题目从中文翻译成了逻辑表达式。

第四步,判断是否需要自连接。如果题目出现“至少两门不同课程”“两位不同的教师”这样的表述,十有八九要复制一份关系并通过重命名区分。这个动作忘记,几乎做不出正确结果。

第五步,写完后反查。查每个属性名是否存在,查结果列是否有多余属性,查自然连接是否让同名列只剩一列,查选择条件引用属性时是否加了必要的表名前缀。这一遍检查花不了两分钟,但能捞回不少分。

下面用三个真实风格的综合题,把这个流程走一遍。

5.2 实例一:“查询选修了全部课程的学生的姓名”

题目里出现“全部”,优先考虑除运算。结果为“姓名”,姓名在Student表;条件和全部课程有关,涉及SC和Course。于是被除数是π(sno, cno)(SC),除数是π(cno)(Course)。除法得到学号集合,再和Student自然连接得到完整学生信息,最终投影sname。

完整表达式:π(sname)(Student ⋈ (π(sno, cno)(SC) ÷ π(cno)(Course)))。这里有一个细节:Student和除运算结果共同的属性是sno,所以自然连接可行。如果你写成等值连接,就需要额外写清Student.sno = 结果.sno,而结果关系名一般是未命名的,不如自然连接干净。因此只要公共属性同名且想要合并列,我就直接用自然连接。

验证方式可以对照SQL:用SELECT sno FROM SC GROUP BY sno HAVING COUNT(DISTINCT cno) = (SELECT COUNT(*) FROM Course)查出学号,再连Student。如果表达式算出的学号和这段SQL一致,基本可以确认正确。这种交叉验证在平时练习中很有用。

5.3 实例二:“查询至少选修了C1、C2两门课程的学生学号”

“至少”在这里不是全部课程,而是指定课程集合。题目只给了C1、C2两个课程编号,不需要Course表参与。有三种等价写法。

第一种用除运算:构造一个临时的除数关系S(cno),包含两行('C1')和('C2'),然后π(sno, cno)(SC) ÷ S(cno)。第二种用交运算:查选了C1的学生集合和选了C2的学生集合,取交集。表达式为π(sno)(σ(cno='C1')(SC)) ∩ π(sno)(σ(cno='C2')(SC))。第三种用自连接:把SC复制成两份,一张表选C1,另一张选C2,然后按学号相等连接。三种写法都成立,考试时选自己最熟悉、最不容易写错的那种。

如果题目是“至少选修了两门课程”,而不是指定的两门,那就不能用固定C1、C2了。这时需要自连接:把SC复制为SC1和SC2,条件是学号相同但课程号不同,然后投影学号。表达式的核心是σ(SC1.sno=SC2.sno AND SC1.cno≠SC2.cno)(SC1×SC2),再投影sno。这一步要特别注意课程号不等条件,否则会把同一课程的两条记录也当成两门课。

5.4 实例三:用差运算表达否定查询

再看“查询没有选修任何课程的学生姓名”。这里“没有”的否定语义对应差运算。一个人的名字要想出现在结果里,前提是他从未出现在选课表SC中。先取Student全部学号:π(sno)(Student);再取SC中出现的全部学号:π(sno)(SC);两者相减,得到没选任何课程的学号:π(sno)(Student) − π(sno)(SC)。

然后连接Student投影姓名:π(sname)(Student ⋈ (π(sno)(Student) − π(sno)(SC)))。要注意的是,相减前两个操作数必须模式一致,所以都对sno投影。如果有人直接写Student − SC,这是非法的,因为Student和SC属性个数不同,无法做差。很多人到了这一步还反应不过来,其实想想集合运算“相同模式才能比较”这个前提就懂了。

同理,“一门课都没挂过的学生”这种否定题,也可以转化为“所有成绩都≥60的学生”,或者“存在成绩<60的学生”取补集。当你会用差运算表达“没有”之后,这类题基本都能做。关键是先把“存在的集合”找出来,再用全体减去它。

6. 关系代数与SQL的对照:理解越深,写SQL越稳

6.1 一条SELECT语句对应一组关系代数算子

很多同学是先会SQL后学关系代数,觉得后者只是理论。实际上关系代数是SQL的“编译目标”,数据库执行一条SELECT时,内部就会生成一棵由选择、投影、连接等算子组成的查询树。把两者的对应关系理清楚,对写SQL和理解优化器都有帮助。

对应关系大致是:SELECT 列对应π,FROM 多表对应笛卡尔积或连接,WHERE 条件对应σ,UNION对应∪,EXCEPT对应−,INTERSECT对应∩,JOIN...ON对应θ连接,NATURAL JOIN对应自然连接。GROUP BY和HAVING在基本关系代数里没有直接对应,所以涉及“分组后计数”的查询,关系代数表达起来比较吃力,一般会借助除法或自连接来替代。

这个对照关系在考试里的直接价值是:遇到一条很复杂的SQL,先在草稿纸上写出它的关系代数骨架,再回过来看SQL,能快速定位多表连接时少了什么选择条件。比如SELECT sname FROM Student, SC WHERE Student.sno=SC.sno AND SC.cno='C1',翻译成关系代数就是π(sname)(σ(cno='C1')(Student ⋈ SC)),写出来之后你很容易发现,先对SC做选择再连接,比先连接再选择效率更高,这也是优化器常用的等价变换。

6.2 等价变换与查询优化思路

考试喜欢考这样一类题:给定两个关系代数表达式,让你判断是否等价。其中最经典的是选择下推和投影下推。

选择下推:σF(R⋈S)等价于(σF(R))⋈S,但前提是F中引用的属性都属于R,或者都属于S;如果F同时引用了两个表的属性,比如R.A=S.A,那就不能下推,因为它本身就是连接条件。这个规则背后的思路很朴素:先把一组数据过滤到更小,再与其他表连接,中间结果会小很多。

投影下推:πL(R⋈S)可以先对R和S分别投影,但必须保证连接属性还在投影列表中。比如Student⋈SC,公共属性是sno,如果你想先投影Student为π(sname, sno)(Student),那没问题,因为sno和sname都在;但如果投影列表里没有sno,后续就没法做连接了。考试里经常用这种题考你对连接属性是否敏感。

还要记住选择和投影的交换限制。πL(σF(R))如果想改写成σF(πL(R)),前提是F用到的所有属性必须包含在L里。如果F用了sdept,而L里只有sname,交换后条件就找不到属性了。所以标准策略永远是把σ放在最内层、π放在外层,除非题目专门让你讨论可交换性。

6.3 用关系代数检查SQL逻辑的正确性

这个习惯是我在实际项目里养成的。有一次接手别人写的多表报表SQL,数据总是对不上,肉眼扫过去看不出哪里不对。我把SQL翻译成关系代数后发现,他用了普通的等值连接,但两张表存在一对多的父子关系,导致某个指标被重复累计。如果直接用SQL纠错,可能要一行行看数据;但用关系代数看连接后的集合语义,一下就明白了:一对多连接会让父表数据复制到每一个子记录上,后续再聚合就会放大。

在备考阶段,你也可以用这个思路验证自己的表达式。写完一个除法表达式,不确定对错时,把关系代数翻译成SQL去执行,或者反过来把SQL执行结果和关系代数手算结果对比。多来几次,你对每个运算的语义会越来越敏感。特别是除运算,很多人觉得它抽象,一旦用SQL里的GROUP BY加COUNT(DISTINCT cno)对照,马上就知道结果长什么样了。

7. 考前最后的自查:考点清单、两道演练、一点心得

7.1 高频考点自查清单

到了考前冲刺阶段,不建议再抱着书从头翻。我给自己列过一张关系代数的自查表,每条都能快速回答,才敢说基本准备到位。

第一,五个基本运算的定义和符号要能默写,并且知道交、连接、除都可以用基本运算推导。第二,两个关系能做并差交的前提是模式相容,属性个数相同且域对应;这个条件有90%的上午题会绕一下。第三,自然连接和等值连接的列数区别要会算。第四,看到“全部”“至少”“没有”能立刻映射到除、交/除、差。第五,σ和π的交换需要满足什么条件。第六,自连接怎样通过重命名实现。第七,选择下推能省多少资源,等价表达式怎么变形。

如果这些条目里有任何一条需要想半天,就说明它还处于模糊状态。不要去刷大量新题,先回到对应章节把那条概念吃透,再找三五道针对性题目巩固,效果比盲目刷题好得多。

7.2 两道限时演练

给你两道适合考前限时训练的题,每道控制在五分钟内。

第一道(上午题风格):有关系R(A, B, C)和S(B, C, D),两个关系的所有元组均不相同。求R和S自然连接的结果属性列表,并说明如果是等值连接,条件为R.B=S.B AND R.C=S.C,结果属性列表是什么。答案:自然连接结果属性是A, B, C, D,共4列;等值连接结果是A, B, C, B, C, D,共6列。这里最容易写错的是把自然连接后面追加S.D,忘记B、C合并。

第二道(下午题风格):已知Student(sno, sname)、Course(cno, cname)、SC(sno, cno),写出“查询没有选修任何课程的学生的学号和姓名”的关系代数表达式。参考答案:π(sno, sname)(Student ⋈ (π(sno)(Student) − π(sno)(SC)))。注意差运算两侧必须是同模式,所以不能直接Student−SC;最终和Student连接时,公共属性sno能够触发自然连接,结果只保留学号、姓名两列,正好符合题意。

7.3 我备考关系代数时的一点经验

最后分享一个我自己用过的笨办法。每天找三道中文查询题,先不急着看答案,把每道用自然语言重写一遍,明确它的动词、名词和量词,再转换成关系代数表达式,最后用SQL验证一次。这个流程坚持两周,效果会非常明显。到考试后期,我只要看到“全部”两个字,脑子里能立刻反射出“被除数、除数、候选值、失配候选值”这一整条链,看到“没有”就自动想“全体减去存在集合”。

还有一个小技巧:考试答题时,尽量把中间结果另起一行写出来,不要全部挤在一行。比如先写T1=π(sno,cno)(SC),再写T2=π(cno)(Course),最后写T1÷T2。这样就算最终表达式有一点错误,阅卷时也能看到你的思路,帮你拿到步骤分。关系代数的本质是把集合操作拆成清晰的步骤,你写得越清楚,得分的机会就越大。

返回列表