数据库系统这门课,大概是计科本科阶段最容易被低估的一门硬课。很多人以为它等于“学SQL”“背范式”,结果期末一看到关系代数的除运算、多表嵌套查询、事务隔离级别就懵了。我在西电计科复习数据库系统的时候,也走过一段弯路——刚开始对着《数据库系统概论》第6版(王珊老师那本经典教材)从头到尾划重点,划到第十章发现前面全忘了。后来换了思路,把整门课拆成几个知识块,对照期末题型逐个击破,才真正把分数稳住了。
这篇笔记不是教材的重抄,而是我完整刷完一遍书、两遍历年题之后整理的复习思路,重点放在“考试怎么考”和“我怎么答”上。适用范围不局限于西电,只要是按王珊教材体系开的数据库系统原理课,基本都能参考。不管你是刚开始第一轮通读,还是考前突击,都可以按下面的章节优先级来安排时间。
1. 这门课到底在考什么?先别急着刷题
1.1 数据库系统不只是SQL,是一整套数据管理思维
我先说一个很多人的误区:数据库系统这门课,SQL只是表面,真正考的是“关系模型下的数据管理思维”。具体来说,就是你怎么把现实世界的业务数据,用严格的数学工具描述出来、存进系统里、再做高效查询和可靠更新。
整个课程体系其实是一条线下来的:
- 数据模型层:关系模型为什么能取代层次模型和网状模型,靠的是数学基础(集合论、谓词逻辑)和简单统一的数据结构(二维表)。考试喜欢考关系模型的三个要素:数据结构、关系操作集合、完整性约束。
- 查询表达层:关系代数(过程性语言)和SQL(描述性语言)解决“怎么查”,前者是理论工具,后者是工程实现。二者可以互相转换,考试经常要求“把SQL转成关系代数”或反过来。
- 设计方法层:ER模型解决“怎么设计表”,范式理论解决“设计的表好不好”,函数依赖是判断依据。这里考的已经不是记忆,而是分析能力。
- 系统实现层:查询优化、事务管理、并发控制、故障恢复,这些是数据库管理系统内部做的事,也是最抽象、最容易丢分的地方,因为它们解决的是“系统怎么保证正确性和效率”。
西电计科的期末风格,我个人感觉是“重推导、重设计、重分析”。选择题和填空题覆盖概念细节,大题基本是关系代数、SQL编写、范式判断与分解、ER图设计、并发调度/事务恢复的分析题。明白了这个格局,你才知道精力往哪里放。
1.2 期末题型决定了复习顺序
我习惯把题库里的题按题型归好类再去复习对应知识点,这样效率最高。根据我手头的历年题,大致可以归成下面这几类:
| 题型 | 常见问法 | 对应知识点 | 分值占比(估计) |
|---|---|---|---|
| 选择/填空 | 概念辨析、术语填空 | 全书各章 | 20%左右 |
| 关系代数/元组演算 | 写表达式、化简 | 第2章 关系运算 | 10%-15% |
| SQL编程 | 建表、查询、视图、授权 | 第3-5章 SQL | 15%-20% |
| 范式分析与分解 | 判断范式、求候选码、3NF分解 | 第6章 关系数据理论 | 15%左右 |
| 数据库设计 | 画ER图、转关系模式 | 第7章 数据库设计 | 10%-15% |
| 事务与并发 | 调度的可串行性判断、加锁 | 第11章 并发控制 | 10%-15% |
| 故障恢复 | 日志分析、重做/撤销 | 第12章 数据库恢复 | 5%-10% |
这个表的价值在于:你可以按分值占比分配复习时间,而不是从第一章一路平推。我第二轮复习基本是倒着来的——先把事务并发和恢复搞定,再去做范式题,最后补SQL和关系代数的手感。
2. 六类必考知识点的复习路径拆解
2.1 关系代数:不只是“查数据”,是对查询能力的极限拷问
关系代数是数据库理论的起点,也是很多人第一个卡住的点。我一开始也觉得,既然SQL能查数据,为什么还要学关系代数?后来复习到查询优化才明白,关系代数是对“查询能力”的数学抽象,优化器就是把SQL转成语义等价的代数表达式再挑一个执行代价最小的。
期末考关系代数,核心就是六种运算:选择(σ)、投影(π)、并(∪)、差(-)、笛卡尔积(×)、更名(ρ),外加连接(⋈)、除(÷)等派生运算。
一个我踩过坑的地方:写选择条件时条件里引用属性要加下标,比如σ_Sno='2015061101'(Student),σ和π的优先级容易弄混。考试里最经典的易错点是:投影之后再选择,和选择之后再投影,结果可能完全不同——因为投影会去掉某些列,而这些列可能正是选择条件需要的。
除运算(Division)是重灾区。我建议不要死记定义,而是理解“被除数 ÷ 除数 = 商”的实际含义:商是那些“对所有除数中的值都满足匹配”的元组。比如查询“选修了全部课程的学生学号”,就是 选修表 ÷ 课程表(投影出课程号)。复习时把除运算拆成三步:先在除数上做投影,再用被除数与商做笛卡尔积,最后做差,这样不会错。
2.2 SQL编程:从会写到会“读题”,二级嵌套是分水岭
SQL这部分,期末题不难,但很烦。烦在题目长、条件多、还经常要求“用两种写法实现同一个查询”。
我的经验是:SQL复习不要只背语法,要按查询类型建立模板。单表查询无外乎SELECT-FROM-WHERE-GROUP BY-HAVING-ORDER BY,重点是分组过滤里WHERE和HAVING的分工——WHERE在分组前过滤行,HAVING在分组后过滤组。很多人写“找出平均成绩大于90的学生”时,把条件写成WHERE AVG(score)>90,这是错的,必须用HAVING。
多表查询里,连接查询和嵌套查询是必考点。考试风格是要求“既会用连接写,也会用嵌套写”。比如“查询选了数据库课程的学生姓名”,可以用自然连接,也可以用IN嵌套。两个都会写,才算这道题稳了。
嵌套查询的分水岭在于二级嵌套。典型题目是“查询比所有计算机系学生年龄都小的学生”——你需要先用一个子查询算出计算机系的最小年龄,再作为外层查询的比较条件。这里注意两点:第一,子查询前可以加ALL、ANY、EXISTS这类量词;第二,相关子查询和非相关子查询的执行逻辑完全不同,考试特别喜欢问“这个查询执行了几次”。
视图和授权虽然分值不高,但容易考简答或填空,比如“视图和基本表的区别”“GRANT和REVOKE的语法”。这块我建议快速过一遍即可,不要花太多时间。
2.3 范式理论:函数依赖是一切的基础
范式理论是数据库系统里最“数学”的部分,也是西电期末考试里区分度最高的一章。复习这一章,我建议唯一正确的姿势是:先吃透函数依赖的定义,再去做范式判断题。
函数依赖X→Y,意思是“X的值确定时,Y的值也唯一确定”。判断函数依赖是否成立,不能靠猜,要按定义验证:在关系r中,任意两个元组,如果X值相等,Y值必须也相等,这个依赖才成立。这个点考试爱考选择题,比如给一个关系实例,问哪个函数依赖成立——这种题只能老老实实去比对。
候选码的求法是范式题的第一步。我的做法是套“属性分类法”:先把所有属性分成四类(只在函数依赖左边出现的、只在右边出现的、两边都出现的、两边都没出现的),然后从“只在左边出现的属性”出发,求闭包,看能不能覆盖全部属性。求闭包就是反复利用Armstrong公理系统,考场上千万别跳步,跳一步就错。
判断范式级别也有一个我常用的快捷路径:先找有没有非主属性对码的部分依赖(有就是1NF),再看有没有传递依赖(有就是2NF),再看有没有非主属性对码的完全依赖以外的依赖关系,最后看是否存在主属性对码的部分依赖。这样逐级往上判断,不容易漏。
3NF分解是范式题的大题,典型考法是“给定关系模式和函数依赖集,判断属于第几范式,若不是3NF则分解为3NF”。这里有一个比较稳妥的算法:先求最小函数依赖集,再将每个依赖X→Y单独成表。但直接按这个方法分解得到的表可能不是无损连接的,所以要加上候选码所在的关系表。考试时我建议先检查分解是否具有无损连接性和依赖保持性,这两个性质是阅卷时的评分点。
3. 事务、并发控制与恢复:理解“为什么”才能拿满分
3.1 ACID其实是一条逻辑链条
事务这章概念多,但理解透了之后答题很爽,因为逻辑是连贯的:事务是数据库操作的基本执行单位,它必须满足原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
我复习时发现一个很好的记忆方式:这四条不是一个平级列表,而是一条因果链。原子性保证事务要么全做要么全不做,一致性保证事务执行前后数据库的完整性约束没有被破坏,隔离性保证并发执行的事务互不干扰,持久性保证事务一旦提交,结果就不会丢失。考试爱考“数据库系统是如何实现这四性的”——答案是:原子性由日志(undo)实现,持久性由日志(redo)实现,隔离性由并发控制(锁/时间戳)实现,一致性由应用层+完整性约束共同保证。
这道题几乎年年考,答的时候不能只写概念,要把“怎么实现”这个层次答出来。
3.2 锁协议:从两段锁到死锁处理
并发控制的核心目标,是让并发调度的执行结果等价于某个串行调度的结果,这样的调度称为可串行化的。考试通常会给你一个调度(一系列读写操作),让你判断它是否可串行化。
判断方法有两个:冲突等价性分析和优先图(前驱图)。优先图法我认为是最稳的:把所有事务画成节点,如果事务T1的某个操作与T2的某个操作冲突(读写同一数据项且至少一个是写),且T1的操作在T2之前,那就画一条从T1到T2的有向边。图里有环,就不可串行化;无环,就是冲突可串行化的。
锁协议部分,重点理解两段锁协议(2PL):事务分为加锁阶段和解锁阶段,加锁阶段不能解锁,解锁阶段不能加锁。两段锁协议是保证冲突可串行化的充分条件,但不是必要条件——这意味着存在某些可串行化调度不满足两段锁,所以考试常出判断题。这里有一个拿分技巧:画事务加锁/解锁的时间线,看有没有违反“先加后解、加完再解”的规则。
死锁的处理也是考点:死锁预防(一次性加锁、顺序加锁)和死锁检测(等待图检测)。我当年复习时老把这两个搞混,后来用一句话区分:预防是“让死锁不可能发生”,检测是“允许死锁发生,但发生后能发现并解除”。
3.3 日志恢复:把崩溃现场还原出来
数据库恢复这章,分值不高但必考,而且考的题型非常固定:给你一个日志序列(含事务开始、读写、提交、检查点等记录),让你分析在某个故障点之后要做哪些重做(redo)和撤销(undo)。
我先说答案逻辑:先扫描日志,确定所有“已提交”的事务和“未提交”的事务。已提交但数据可能没写回磁盘的,要重做;未提交但可能已改了缓冲区的,要撤销。具体用哪种策略,取决于日志采用什么方式(立即修改还是延迟修改,有没有检查点)。
检查点是这章的难点。有了检查点后,恢复时不需要扫描全部日志,只需要从最近一个检查点开始处理即可。我建议用一个固定流程来做这类题:先定位最近检查点,再在检查点之后找所有事务的状态(提交了还是没提交),最后分别做redo/undo。这样做题不容易乱。
复习这章时,可以在纸上画一个“日志记录时间轴”,把每条日志、检查点、故障点都标上去,然后一步步推导。我备考时画了近十道题的推导过程,考试时看到类似题几乎是机械作答,正确率很高。
4. 数据库设计题:从ER图到关系模式的完整链路
4.1 ER图画法:实体、联系、属性之间别犯晕
数据库设计题,一般是给你一段业务描述,让你画出ER图,再转成关系模式,并指出主码和外码。这种题看着简单,但丢分的人不少,问题大多出在“实体与属性的边界”上。
我总结了一个判断技巧:如果一个事物有自己的独立属性(尤其是多值属性或复合属性),或者需要被多个实体共享,那么它更可能是一个实体;如果它只是描述某个实体的特征,且没有独立的存在意义,那就是属性。比如“学生”有“班级”,如果只需要记录班级名,它是属性;如果需要查询“班级的班主任”“班级的教室”,它就应该提升为实体。
联系类型(1:1、1:n、m:n)的判断是另一个易错点。方法很简单:从双方的视角各问一句“一个A对应几个B”“一个B对应几个A”。比如“一个学生选修多门课程,一门课程被多个学生选修”,那学生和课程之间就是m:n联系。这里要注意的是,m:n联系在转关系模式时,需要单独建一张联系表,表中包含双方主码和联系属性。
4.2 一个真实训练:从需求描述到3NF的关系模式
我来用一个具体的例子演示完整的设计链路,这也是我复习时自己练过的题。
需求描述:某学校要管理教师授课信息。每位教师有工号、姓名、职称;每门课程有课程号、课程名、学分;一位教师可以讲授多门课程,一门课程可以由多位教师讲授;教师讲授某门课程会产生一个“课时数”属性。
第一步画ER图:教师实体(工号为主码)、课程实体(课程号为主码),二者是m:n联系“讲授”,联系属性是课时数。
第二步转关系模式:教师(工号,姓名,职称),课程(课程号,课程名,学分),讲授(工号,课程号,课时数)。讲授表的主码是(工号,课程号),两个属性分别引用教师和课程的主码,作为外码。
第三步检查范式:教师表里,是否存在“工号→职称”以外的传递依赖?如果职称和某种等级挂钩(比如“教授”对应“博导资格”),需要结合实际看是否拆分。课程表同理。讲授表的主码是联合主码,唯一依赖就是(工号,课程号)→课时数,所以是BCNF。
这道题跑通之后,我对ER转关系模式的理解就从“背规则”变成了“有手感”。建议你也找两三道类似的题完整写一遍。
4.3 设计题简答题的目标:解释清楚“为什么这么设计”
有些学校(西电也这样)会在设计题后面跟一问:“为什么该关系模式需要满足3NF(或BCNF)?”或者“如果某个表不满足范式,会有什么问题?”
这种简答题考的是对范式的理解深度。我建议记住三个关键词:冗余、更新异常、插入/删除异常。不满足范式会导致数据冗余存储;修改某条数据时要改多处,容易不一致;插入新数据时可能因为主码不完整而无法插入,删除数据时可能把不该删的信息也删掉了。答题时先把这三个问题写出来,再解释3NF如何通过消除部分依赖和传递依赖来缓解这些问题,就能拿全分。
这里有一个进阶技巧:把“范式设计”和“工程代价”连起来说。比如BCNF虽然消除了所有基于函数依赖的冗余,但可能破坏依赖保持性,实际项目中有时会特意保留到3NF而不是BCNF,就是为了让更新检查更高效。这种回答在老师眼里就是“真的理解了”,不是背答案。
5. 期末复习时间轴与刷题策略
5.1 第一轮:教材通读与课后题
我备考用的教材是王珊的《数据库系统概论》第6版,这本书课后题质量很高,尤其是关系代数、SQL、范式和并发控制那几章的习题,和期末难度非常接近。
第一轮复习我的节奏是每两天一章,重点章(关系代数、SQL、范式、并发)放三天。通读时只做一件事:把每章的核心概念用自己的话写在一张A4纸上,不抄定义,而是写“这个知识点解决什么问题、和前后章节有什么关系”。比如看到“两段锁协议”,就写“它用来保证并发调度可串行化,通过限制加锁/解锁顺序来实现”。
课后题全部手写。尤其是范式分解题,别看答案觉得自己会了,上考场就露馅——必须动手推导一遍完整过程,包括闭包计算、候选码求解、分解结果验证。
5.2 第二轮:真题与错题专项
第二轮开始做历年真题。我建议严格控制时间,按考试标准2小时完成一套,再对答案。对答案时不要只看对错,要分析错因:是概念不清、推导跳步、还是题目理解偏了。
错题整理我有一个习惯:不抄原题,只抄“错误的知识点+正确的思考路径”。比如“除运算的商忘记先对除数去重”,就写“除运算:先对除数的连接属性做投影再去重”。考前翻这页比翻书高效得多。
如果某个知识点反复错,就回到教材对应章节做三到五道同类题,直到形成肌肉记忆。我第二轮结束时,关系代数和SQL基本能做到满分,范式题从最早的一道错一半变成稳定全对。
5.3 第三轮:模拟与查漏补缺
第三轮我一般安排在考前两三天。这个阶段不做新题,只做两件事:
第一,把错题本从头到尾过一遍,每道错题都要能不看答案说出完整解题过程和关键注意点。第二,做一套整卷模拟(最好是没做过的真题),全程计时,模拟考场状态。
模拟时特别要注意时间分配。我的策略是:选择和填空控制在25分钟内,关系代数和SQL控制在40分钟内,范式题25分钟,设计题20分钟,并发和恢复30分钟,留10分钟检查。这个分配不一定适合所有人,但至少你要有意识地控制每类题的用时上限,避免被某道难题卡住。
6. 我踩过的坑与复盘建议
6.1 讲义与教材不一致时,以教材为准对答案
复习数据库系统时,我犯过一个错误:老师PPT上有一句话“3NF分解算法分为两步”,我照这个去套一道课后题,发现分解结果和答案不一致。后来仔细对比才发现,PPT的算法是简化版,少了“检查无损连接性”这一步,而教材上的标准算法是完整版。
所以,任何有歧义的结论,都以教材为准。王珊的第6版是经过多年打磨的经典版本,表述规范、算法完整,期末考试命题基本以教材为准。老师的PPT是提炼版,方便课堂讲解,但遇到细节问题时还是回归教材最稳妥。
6.2 范式判断容易丢分的几个细节
范式判断的隐藏坑不少,我挑几个最常踩的:
第一,候选码不止一个。求候选码时千万别找到一组就停,要检查有没有多个候选码。比如关系模式R(A,B,C,D),函数依赖集为AB→C、C→D、D→A,这里候选码有三个:AB、BC、BD,漏掉一个会导致后面判断主属性和非主属性全错。
第二,主属性和非主属性的划分要在所有候选码的基础上做。只要某个属性出现在任何一个候选码中,它就是主属性。判断2NF时,部分依赖是指非主属性对码的部分依赖,和主属性无关。
第三,分解成3NF后,不一定满足BCNF。考试偶尔会追问你“能否继续分解到BCNF”,这时候要会用“违反BCNF的依赖X→Y,把X和Y单独成表”的方法继续分解,同时验证分解是否依赖保持。
6.3 考场上时间分配与答题规范
最后说说考场上的实操经验。数据库系统的计算和推导题多,答题规范直接影响得分,尤其是范式分解和并发调度这类题。
写范式分解时,我建议每一步都标注理由。比如“计算属性集闭包:{A}⁺ = {A, B, C, D},故A为候选码”,一句话就能让阅卷老师看清楚你的思路,即使最后结果有偏差,也能拿到过程分。并发调度题画优先图时,一定要用直尺,边画边标冲突操作,避免漏边。
如果遇到不会的题,先跳过,把后面会做的拿到手。我考试时就遇到一道除运算的关系代数大题,第一眼没思路,果断先去做后面的SQL题,等最后再回头用“三步法”慢慢推,结果还是做出来了。实战经验是:数据库系统的大题基本不存在“完全不会”,只有“一时没想通”,跳过再回来往往就有思路了。
我个人的体会是,数据库系统这门课,真正值钱的地方不在背了多少概念,而在你能否把“从现实需求到关系模式,再到查询和事务处理”这条完整链路走通。期末复习是走通这条链路最集中的一次训练,认真对待它,收获的远不止一个分数。