数据库系统概论课后题高效刷法:关系代数、范式与并发控制全攻略

数据库系统概论课后题高效刷法:关系代数、范式与并发控制全攻略 1. 先想清楚这门课的课后题究竟在考什么数据库系统概论第五版几乎是我见过的国内数据库课程里使用范围最广的教材之一尤其对于计算机、软件工程、信息管理等专业王珊老师的这本教材基本是入门必读。很多同学把这本教材的课后习题当作期末考试前抱佛脚用的题库做完一遍就扔结果考试时发现题目看着眼熟但就是写不完整。我个人的观点是课后题的价值远比你以为的大但它训练的不是背答案而是三种底层能力。1.1 这本教材的习题设计逻辑先把整本书的章节结构捋一遍。大致可以分成四个模块基础概念与数据模型前几章绪论、关系数据库、关系数据库标准语言SQL数据库设计与操作关系模式设计、范式、数据库设计过程数据库系统内部机制查询优化、事务、并发控制、恢复技术数据库应用与新技术发展数据库安全、完整性、数据仓库、大数据等。课后题也是按这个逻辑安排的。基础模块的题目以给出关系代数表达式写出SQL语句为主考察的是对关系模型的理解是否到位设计模块偏理论比如判定范式、模式分解考察逻辑推导能力系统内部机制模块的题目则是简答题和设计题并重比如并发调度是否可恢复、日志如何恢复等。所以如果你只把它当成题库会漏掉很大一部分价值。正确的做法是在做题时问自己一个问题这道题想让我理解哪一个课后没明说的知识点1.2 我在带学生时最常用的三道自测题如果你是刚开始学这门课或者已经学完但心里没底我建议先用下面三道题快速自测一下大概判断自己处在什么水平用关系代数表示查询选修了全部课程的学生姓名。给定一组函数依赖判断该关系模式最高属于第几范式并说明理由。给出一个包含两个事务的并发调度判断它是否冲突可串行化。这三道题分别对应三个能力维度关系运算表达能力、规范化理论推演能力、并发控制机制的判断能力。如果你能不看教材在15分钟内比较顺畅地完成且理由清晰那你的基础是相当扎实的如果卡壳了那接下来的章节值得你认真阅读。我做知识付费和课程辅导这么多年见过太多SQL写得溜但范式判断全靠蒙的同学。原因很简单SQL语法是看得见的范式逻辑是看不见的人天然会对看得见的东西产生安全感。所以这篇内容里我会尽量把看不见的部分讲透顺便把课后题的典型套路拆开揉碎给你看。2. 关系模型与SQL题型先拿下这两类送分题这一章的课后题是多数人最不担心的因为会写SQL的人很多。但我批改过大量作业后发现会写SQL和能拿全部分数是两回事。关系代数、元组关系演算、SQL三者之间的等价转换才是老师真正想考的。2.1 关系代数表达式的标准解题顺序很多同学一看到用关系代数表示……就发怵因为关系代数写起来语法别扭。其实它是有固定套路的。我总结的顺序是先定目标列再定筛选条件最后定表连接方式。举个例子。假设有关系模式Student(Sno, Sname, Ssex, Sage, Sdept)Course(Cno, Cname, Ccredit)SC(Sno, Cno, Grade)题目查询选修了数据库系统概论课程的学生学号和姓名。关系代数表达式可以写成π_{Sno, Sname}(σ_{Cname数据库系统概论}(Student ⋈ SC ⋈ Course))但更推荐先做选择再做连接因为这样能减少中间结果的数据量。高效的写法是π_{Sno, Sname}(Student ⋈ (π_{Sno}(SC) ⋈ σ_{Cname数据库系统概论}(Course)))或者更细化一点把SC先和经过选择后的Course做自然连接得到选过这门课的学生学号集合再与Student连接取姓名。这个顺序背后的思想是选择下推在查询优化部分还会出现。常见错误集中在三个地方没有区分自然连接和等值连接导致结果中出现重复列把条件写在连接之前却不加括号运算符优先级弄错忘记去重关系代数中的投影π本身会去重但连接会产生重复元组需要留意。2.2 SQL查询的常见陷阱与改写思路SQL部分经典题型集中在三类嵌套查询、分组统计、除法问题。先说除法问题也就是查询选修了全部课程的学生姓名。很多同学第一反应是写一个WHERE Cno IN (SELECT Cno FROM Course)这是错的。正确思路是用双重否定SELECT Sname FROM Student s WHERE NOT EXISTS ( SELECT 1 FROM Course c WHERE NOT EXISTS ( SELECT 1 FROM SC sc WHERE sc.Sno s.Sno AND sc.Cno c.Cno ) );这个写法的逻辑是不存在这样一门课程该学生没有选修它。也就是没有遗漏任何课程。这个双重否定模式我希望你把它背下来因为它还会演变出很多变体比如选修了张三同学所选全部课程的学生没有选修任何课程的学生等等。再说分组统计。典型题目是查询选修了两门以上课程的学生学号和选课门数SELECT Sno, COUNT(*) AS cnt FROM SC GROUP BY Sno HAVING COUNT(*) 2;这里有个非常常见的错误把COUNT(*) 2写到WHERE里。记住WHERE过滤的是元组HAVING过滤的是分组聚合条件必须放在HAVING中。另一个错误是在SELECT中混入了非分组列且不满足函数依赖比如选出Sname但没有把它放在GROUP BY里在严格模式下会直接报错。我建议你在做SQL题时养成一个习惯先把题干的查询目标和过滤条件拆开再用伪代码写出骨架最后翻译成SQL。这个方法虽然看起来慢但准确率极高考试时也不会因为紧张而漏条件。3. 范式判断与模式分解把理论题变成固定流程到了第三章内容也就是关系模式设计这块很多人的噩梦就开始了。范式判断、候选键求解、模式分解题目看起来千变万化其实解法比SQL还要机械。你只要把流程固定下来每一步都按部就班执行拿分很容易。3.1 候选键求解一切判断的前提范式判断第一步永远是找候选键很多人跳过这一步直接看函数依赖结果必然出错。候选键的求解方法叫属性闭包算法。给定关系模式R(U, F)U是属性集合F是函数依赖集合对某个属性集X它的闭包X⁺是从X出发通过F中的所有依赖能推出的全部属性集合。判断方法很简单如果X⁺ U那么X是一个超键如果X的任何真子集的闭包都不等于U那么X是候选键。我举个非常典型的课后题例子。设关系模式R(U, F)其中U{A,B,C,D}F{A→B, B→C, C→D}。求(A)⁺A→BB→CC→D所以A⁺{A,B,C,D}U。再看A的真子集只有空集显然不够。因此候选键就是A。这个例子虽然简单但它带出一个重要概念候选键可以是一个属性也可以是多个属性的组合。比如另一个常见例子R(Sno, Sname, Cno, Grade)函数依赖F{Sno→Sname, (Sno,Cno)→Grade}。这里的候选键是(Sno,Cno)因为只有组合在一起才能推出全部四个属性。3.2 范式判断的完整流程拿到候选键之后范式判断就水到渠成了。我按从低到高的顺序给你整理判断1NF看所有属性是否都是不可再分的原子值。绝大多数题目默认满足1NF但题目如果描述某属性可以包含集合那就要扣分。判断2NF在1NF基础上消除非主属性对候选键的部分函数依赖。如果候选键是单属性那一定满足2NF因为没有部分可言。判断3NF在2NF基础上消除非主属性对候选键的传递函数依赖。这是最常考的节点。判断BCNF在3NF基础上消除任何属性对候选键的传递依赖或者说每个函数依赖的左部都必须是超键。回到刚才的例子R(A,B,C,D)F{A→B, B→C, C→D}候选键是A。非主属性B、C、D都完全依赖于A所以满足2NF。但是存在传递依赖A→BB→C所以C传递依赖于A同理D也传递依赖于A。因此它不满足3NF最高是2NF。这里我要特别提醒一个高频错误不要把传递依赖和部分依赖搞混。部分依赖是候选键的一部分能决定非主属性比如候选键是AB但单独A就能推出C传递依赖是候选键推出非主属性XX又推出非主属性Y。二者性质完全不同却经常在同一道题里出现所以一定要在卷面上写清楚判断依据。3.3 模式分解无损且保持依赖范式判断之后通常跟着模式分解题。分解的要求一般有两个缺一不可无损连接性分解后自然连接能还原原始关系不产生多余元组保持函数依赖所有函数依赖在分解后的子模式中仍然成立或能被推出。还是用R(A,B,C,D)F{A→B, B→C, C→D}举例候选键是A。因为存在传递依赖我们把它分解为R1(A,B)R2(B,C)R3(C,D)。判断无损连接可以用表格法也可以直观理解R1和R2的公共属性是BB是R2的候选键所以R1⋈R2不会丢失信息同理R2和R3的公共属性是CC是R3的候选键。因此整体无损。再判断依赖保持A→B在R1中B→C在R2中C→D在R3中所有依赖都保留完美。但我要提醒不是所有分解都这么漂亮。比如有交叉传递依赖、多属性候选键时很容易分解后丢失依赖这时需要把丢失的依赖单独拆成一个关系哪怕它与之前有冗余。这是很多同学会漏掉的步骤。4. 事务、并发和恢复简答题怎么答到点子上数据库系统概论后半部分的课后题风格和前几章完全不同。从这章开始题目不再只是写表达式更多是说明为什么判断是否正确设计一个机制。很多同学背了概念但一遇到分析题就不知道怎么组织语言。其实这类题有非常清晰的答题框架。4.1 事务ACID与并发调度判断事务的四个特性——原子性、一致性、隔离性、持久性——几乎每年必考。但课后题很少直接让你默写定义而是喜欢结合场景。比如某事务执行到一半系统崩溃请说明数据库系统如何保证原子性这就要你联系日志和恢复机制来答而不是背原子性是指要么全做要么全不做。并发控制部分最高频的题型是判断一个并发调度是否冲突可串行化。两个操作构成冲突的条件是它们来自不同事务、操作同一个数据项、且其中至少有一个是写操作。判断方法有两种调整交换不相邻但不相冲突的操作看能否得到一个串行调度构造前驱图检查是否有环。我比较推荐前驱图法因为它可视化程度高、不容易漏。画法是如果调度中Ti的某个操作与Tj的某个操作冲突且Ti的操作排在Tj之前则画一条从Ti到Tj的箭头。如果最终图中没有环则调度是冲突可串行化的。举个例子。调度S为T1:R(A), T2:R(A), T1:W(A), T2:W(A), T1:R(B), T2:R(B)。我们找冲突操作T1:R(A)与T2:R(A)不冲突都读T1:R(A)与T2:W(A)冲突且T1在前画T1→T2T1:W(A)与T2:R(A)冲突T1在前画T1→T2T1:W(A)与T2:W(A)冲突画T1→T2。再加上后面的B操作T1:R(B)与T2:R(B)不冲突。最终图中只有T1→T2无环所以该调度冲突可串行化等价于先T1后T2。做题时注意只考虑冲突操作读读之间不用画线。很多同学画了一堆多余的箭头把自己绕晕了。4.2 两段锁协议与死锁的答题要点两段锁协议2PL是保证冲突可串行化的经典方法。关键点是事务分成两个阶段扩展阶段只能加锁(lock)收缩阶段只能解锁(unlock)。一旦开始解锁就不能再申请任何新锁。课后题喜欢问为什么两段锁协议能保证冲突可串行化或者给出一个遵循2PL的锁调度序列。答题时建议分三步指出2PL的核心规则说明任何符合2PL的调度其事务获取最后一把锁的时刻可以把调度切成两个部分进而等价于一个串行调度简单总结因为锁的获取顺序决定了事务间的冲突顺序所以满足冲突可串行化。死锁部分答题要点是区分死锁预防和死锁检测。预防常用一次性锁所有资源或按固定顺序加锁检测则要提到等待图有环即死锁需要选择一个牺牲者回滚。这里有个容易丢分的小细节死锁只可能发生在事务持有锁又要申请新锁的场景所以调度题目中如果所有事务一次性锁完那就不可能死锁要在答案里点明这一点。4.3 故障恢复日志与检查点恢复技术是期末大题的热门候选。核心概念是两类日志UNDO日志和REDO日志以及它们组合成的UNDO/REDO日志。复习时不要只背日志格式要理解为什么需要两类操作如果事务在提交前崩溃需要UNDO撤销未提交事务的修改恢复旧值如果事务已提交但数据页尚未写入磁盘需要REDO重做已提交事务的修改保证持久性。结合检查点时答案要体现出两条规则对未提交事务做UNDO对检查点之后已提交事务做REDO。我建议你做题时在时间轴上画出检查点位置、事务提交时间和崩溃点一份清晰的示意图比一大段文字更让人信服阅卷老师也更容易给分。5. 存储、索引与查询优化别把幕后题当背景板很多同学学到后面的存储和索引章节就开始放松觉得这是数据库内部的事情考试大概不考。但王珊这本书的课后题在存储、索引、查询优化部分是出得相当有水平的综合性很强常常是区分高分和及格分的关键区域。5.1 B树索引习题的适用范围B树相关题目通常考察三个点层数计算、插入删除后的树结构调整、聚簇索引与非聚簇索引的区别。先说层数估算这是最简单的送分题但需要你掌握一个估算流程。假设每条记录100字节一个数据页8KB8192字节那么一页大约能放80条记录。如果表里有100万条记录就需要约12500个数据页。如果索引项键值指针占16字节一个索引页8KB可以放约512个索引项。B树根节点放512个索引项第二层放512×512262144个第三层放512³≈1.34亿个索引项。所以100万条记录用3层B树就足够了。在做题时记住计算公式其实是每层能覆盖的页数相乘。关于插入删除后的结构调整最容易错的是分裂规则。B树插入时如果叶子节点满了要分裂成两个节点并把中间键上移到父节点如果父节点也满了继续递归分裂直到根。删除时如果节点过于稀疏需要向兄弟节点借键或合并。这里请记住B树的所有叶子节点在同一层这也是它适合作为数据库索引的底层原因。5.2 查询优化先选择、后投影、再连接查询优化章节的课后题核心是让书上的启发式优化规则落地。最经典的一类题是给定一条SQL语句画出对应的关系代数语法树然后用启发式规则优化。举个例子。查询选修了数据库系统概论课程且成绩大于90分的学生姓名SQL写成SELECT Sname FROM Student s, SC sc, Course c WHERE s.Sno sc.Sno AND sc.Cno c.Cno AND c.Cname 数据库系统概论 AND sc.Grade 90;初始的关系代数树通常是π_{Sname}(σ_{条件}(Student × SC × Course))优化过程按这样的顺序进行选择下推把Cname数据库系统概论下推到Course节点把Grade 90下推到SC节点尽早缩小数据量投影下推把连接后可能用不到的列提前去掉比如Student表只需要Sno和SnameSC表只需要Sno、Cno、Grade连接顺序调整优先连接数据量小的关系比如先做SC与Course的连接再与Student连接。优化后执行效率通常比三表先做笛卡尔积再筛选高几个数量级。这不仅是课后题答案也是实际数据库优化器的基本思路。答这类题时我建议把每一步都注释清楚为什么这样下推比只画一棵优化后的树更容易拿高分。6. 从刷题到考场课后题的三种刷法光知道知识点还不够把课后题真正变成考场上的得分能力需要讲究方法。我自己带学生时经常强调刷题不是比数量而是比轮次。同样一本课后习题建议你至少过三轮。6.1 第一轮按章节地毯式过基础第一轮学习新知识时我会建议你每学完一章就做对应习题但不要做完就丢。这一轮的目标是知道每道题考什么建议在题目旁边用一句话标注考点比如关系代数除法运算范式判断3NF判定并发冲突可串行化判断。这个动作看起来简单但它能帮你建立题目特征→解题方法的反射。6.2 第二轮跨章节综合刷第二轮放在期中或期末复习阶段。此时不要再按章节顺序刷而是打乱章节混合抽取题目。因为考试从来不会告诉你这道题考的是第三章还是第七章。混合训练时你会发现很多知识点之间有隐蔽的联系比如SQL中的NOT EXISTS在关系代数中对应除法而除法的本质又和全称量词挂钩这在后面的数据库新技术章节还会出现。第二轮做题时建议用答案遮罩法把答案部分完全挡住先凭自己思路写写完再对照标准答案。不要边看答案边做那样会产生我好像会了的错觉。6.3 第三轮只输出关键步骤考前最后几天没有时间完整做每一道题。这时我会用口述思路法随机翻一道题只看题干不看答案然后在心里或者对同学口述这道题的解题步骤比如先求候选键A发现存在传递依赖B→C所以不满足3NF分解成R1(A,B)、R2(B,C)、R3(C,D)检查无损和三依赖保持。如果能顺畅说出思路这道题就算过关如果卡壳就标记出来集中回看。这个方法特别适合时间紧张的同学效率非常高。因为考试阅卷时老师看的就是你的解题思路是否清晰而不是你背了多少原文。6.4 错题本怎么记才能有用很多同学有错题本但记法大多是抄题干抄答案这样效果很差。我更推荐做归因式笔记。每次做错题问自己三个问题是哪一步开始错的是候选键求错了还是范式判断顺序写反了这个错误的深层原因是概念不清还是计算粗心还是没见过这个题型下次怎么做才能避免同类错误把这三点写在错题本上复习时只看归因不看完整答案。你会发现错题本越到后面越薄因为很多错误本质上是同一个问题。7. 我在教学和备考中踩过的一些坑最后聊点题外话是我这些年带学生过程中反复看到的问题也算是一点真实经验。第一个坑是重SQL轻关系代数。很多人觉得关系代数考试分值不多就懒得练。但实际上关系代数是理解查询优化的基础一旦碰到请画出该SQL语句的关系代数语法树并优化这类综合题没有关系代数基础就完全无从下手。哪怕不为考试工作中看执行计划、调慢查询也会用到这套思想。第二个坑是范式判断不看题目给没给候选键。有些教材和题目会直接给出候选键有些不给。不给的时候你非要去求给的时候你反而要留意题目告诉你候选键可能意味着陷阱比如候选键是组合键的时候才可能存在部分依赖。我见过太多学生在题目已经给出候选键的情况下还在闭包运算上花很久时间。第三个坑是考场上一头扎进SQL长语句却不先画关系代数树。实际上很多SQL题在草稿纸上画一棵树把连接顺序、过滤位置标清楚写SQL时就不容易漏条件或写错嵌套层次。这个习惯我强烈建议你从平时练习就养成而不是考前突击。还有一个经验是不要迷信背答案原文。数据库系统概论课后题的标准答案通常很精炼但阅卷老师看的不是关键词而是推导过程的逻辑性。你哪怕用完全不同的步骤得到相同结论只要逻辑正确、过程完整照样给满分。所以复习时一定要保证自己真的能推导而不是只能默写。这本教材的课后题如果你能按我上面说的思路认真过三遍我相信不光是期末考试整个数据库系统的知识框架都会扎实很多。后续学数据库原理、数据库系统工程师甚至做应用开发时再回去翻这些习题你会发现自己能记住的远比想象中多。