2022年10月中旬,我参加了数据库系统原理这门课的第一次阶段考。考完走出考场,脑子里全是关系代数那条除运算的式子——明明复习时看过,可一上考场就写错了。后来我对着参考答案,把试卷从头到尾重抄一遍,又做了两轮错题归类,才有了你现在看到的这份试卷笔记。这篇笔记不是标准答案,也不是押题,它记录的是我在2022年10月这次考试前后的真实整理:考了什么、哪些地方反复被坑、以及后来我怎么用一张卷子变成三轮复习资料。如果你正在学数据库系统概论、数据库系统概念这类教材,或者马上要面对一场本科数据库考试,这份思路应该能帮你少走不少弯路。
1. 2022年10月这次考试到底考了什么:整卷结构复盘
那次考试我们学校是满分100分、120分钟,大概题型分布是:单选12题24分、填空8题16分、简答2题12分、综合4题48分。这个结构比较典型,选择题和填空题抠概念,简答题考事务和并发控制,综合题则集中在ER图、关系代数、SQL和范式分解上。下面是我根据回忆整理出来的题型分值表,不是原卷,但基本能还原当时考卷的难度分布。
| 题型 | 题量 | 分值 | 主要覆盖内容 |
|---|---|---|---|
| 单选题 | 12题 | 24分 | 三级模式结构、关系完整性、SQL语义、索引类型 |
| 填空题 | 8题 | 16分 | 候选码、外码、函数依赖、事务ACID、两阶段锁 |
| 简答题 | 2题 | 12分 | 事务的含义与ACID、可串行化调度的概念 |
| 综合题 | 4题 | 48分 | ER图转关系模式、关系代数表达式、SQL查询、范式分解 |
1.1 每个题型的真实体感
单选题看着轻松,其实最容易翻车。它不会直接问你“什么是候选码”,而是给一个关系模式,里面藏着函数依赖,问你“下列哪个属性组合不能作为超码”。这种题目需要你先把候选码算出来再去排除选项。我记得有一道选择题是问数据库三级模式结构中,用户看到的是哪一层,选项把内模式、模式、外模式都摆在一起,如果平时只看概念没理解清楚,很容易把“模式”和“外模式”搞混。填空题则更强调记忆,像“实体完整性要求主码不能取____”,填“空值”;“外码要么为空,要么等于被参照表中某个元组的主码值”,这种原文填空题只能靠背书,没什么技巧。
简答题在10月这次考试中并没有出得很偏,问的是“什么是事务?事务有哪些ACID性质”。这类题其实是在送分,但很多人拿不到满分,因为只写了四个词而没有解释每个性质的含义。阅卷老师想看的是:原子性意味着事务中的操作要么全做要么全不做,一致性是事务结束后数据库从一个正确状态到另一个正确状态,隔离性是并发执行的事务互相不干扰,持久性是事务一旦提交结果就不会丢。只写“原子、一致、隔离、持久”四个字,大概率要扣一半分。
综合题是重头戏,4道题几乎每一道都不白给。关系代数那道题要求用除运算表达“查询选修了全部课程的学生学号”,我当时写出了选课关系除以课程关系,但把除法和等值连接的优先级理解错,导致整体表达式多套了一层括号,结果被判了半对。ER图题给了一个学生、课程、选课的局部ER图,要求转换成关系模式,这个还好,但里面埋了个“教师在哪个系任教”的1:N联系,有人把系编号放到了教师表里,有人却单独建了一张“任教”表被扣分。SQL综合题考了分组聚合加HAVING过滤,还加了一小问窗口函数,当时我们教材课上没细讲,很多人直接空着。
1.2 从试卷反推回来的教材章节图谱
如果你用的是王珊《数据库系统概论》第六版,那么10月这次考试覆盖的章节非常集中,基本是:第2章关系数据库、第3章关系数据库标准语言SQL、第6章关系数据理论、第7章数据库设计、第11章并发控制。选择题还会零星涉及第1章绪论和第4章数据库安全性。这里有一个容易被忽略的点:第9章“关系查询处理和查询优化”里关于在B+树索引上执行查询的过程,是选择题和填空题的高频来源,很多同学只看前三章和范式,结果索引部分被扣了很多分。
我这个阶段考之后才深刻意识到,所谓“试卷笔记”不是简单抄题,而是要把每一道题对应到教材的具体知识点上。比如SQL那道综合题,对应王珊第三、五章里关于数据查询语句的语法;范式分解那道题对应第六章关系数据理论中的分解准则;事务和隔离级别对应第十一章并发控制。如果能把每道题都标注教材页码,后续复习时就能快速定位自己的薄弱点。用《数据库系统概念》第七版的同学也类似,只不过章节编号不同,但关系模型、SQL、E-R模型、事务管理这几大块是永恒的。
2. 三座大山:ER图转关系模式、关系代数、SQL的典型题解
很多同学看到这里会觉得“这些东西书上都写了”,但真题的坑恰恰出在书上最不起眼的细节。我在这三门“安全知识体系”上栽过跟头,所以单独开一节展开讲。
2.1 ER图转关系模式:最容易丢分的步骤
当时试卷有一道ER图题,给的是“学生-课程-选课”模型,外加一个“系”实体。标准做法其实很简单:每个实体对应一张表,实体的属性就是表的列;M:N联系必须单独建表,1:N联系则在N端加上外码,1:1联系可以并入任一端。
学生和课程之间是M:N“选修”联系,联系本身有属性“成绩”,所以必须建立选课表SC,主码是学生学号+课程号,同时这两个字段分别作为外码引用学生表和课程表。写成关系模式就是:
Student(sno, sname, dept)
Course(cno, cname, credit)
SC(sno, cno, grade),主码(sno, cno),外码sno和cno。
这里最大的坑是有人把“成绩”属性直接放到Student表或者Course表里。在E-R模型里,联系上的属性只能放在联系转换出的表里,放错地方意味着数据冗余爆炸:如果一个学生选十门课,成绩放在学生表里就得重复十行学生信息。我当时没犯这个错,但我的一个室友把成绩放到了Course表,理由是“一门课所有学生都相同成绩”,显然不对。
另一道隐藏考点是关于系、教师、学生的1:N联系。规则是:一个系有多名教师,一名教师只属于一个系,那么要把系编号作为外码放在教师表里,而不是单独建“教师-系”联系表。单独建表虽然不违反语义,但冗余了一堆关联行,数据库设计题通常会扣分。如果是1:1联系,比如班主任和班级,可以把班主任号放在班级表里,或者把班级号放在教师表里,选择一端添加外码即可,不需要独立建表。考试最喜欢在这种地方埋坑。
2.2 关系代数表达式:除运算和自然连接的陷阱
关系代数里的除运算,是每次考试的分水岭。题目问“查询选修了全部课程的学生学号”,正确写法是:
π_sno, cno(SC) ÷ π_cno(Course)
理解起来很直观:SC是学生选课记录,Course是所有课程,两者做除法,结果就是那些“选的课覆盖了全部课程”的学生。但考试时大家容易卡在两点:一是忘记对SC先做投影,把成绩列也留下来,导致除法的除数、被除数属性集合对不上;二是把除法的结果再去做自然连接,多此一举。关系代数里不存在GROUP BY,也不需要“having count(...) = (select count(*) from course)”这种SQL写法的替代品,它只能靠除运算表达“所有”这个语义。
另一个经典题目是“查询没有选修任何课程的学生姓名”,这个必须用差运算:
π_sno(Student) − π_sno(SC)
先求出所有学生学号,再减去选过课的学生学号,剩下的就是没选课的人,最后再和Student表自然连接取出姓名。这里有个隐藏坑:如果学生表中存在没有选课的学号,而SC表中也有一个不被Student引用的脏学号(现实中不该出现,但题目会故意放),差运算会基于集合语义正确处理。关系代数对空值和重复元组是敏感的,这一点和SQL的默认行为不同,做题时务必把投影列写清楚,不要贪图省略。
2.3 SQL综合题:视图、分组与子查询的组合拳
SQL题几乎是必考的“查询平均成绩大于85的课程号和平均成绩”,用分组加过滤:
SELECT cno, AVG(grade) AS avg_grade FROM SC GROUP BY cno HAVING AVG(grade) >= 85;
这个题失分点集中在两处:一是在GROUP BY之后误把非分组列写进SELECT,比如还把sno写出来,违反分组语义;二是不知道HAVING和WHERE的先后关系。记住:WHERE是分组前过滤元组,HAVING是分组后过滤组。如果先用WHERE把低于60分的记录删掉再去算平均分,和保留所有记录算平均分再过滤,结果完全不同。考试时看到“平均成绩”脑子里必须浮现GROUP BY、HAVING这对组合。
那道加分性质的窗口函数题是这样的:查询每门课程前两名的学生学号和成绩。标准写法是用RANK()窗口函数:
SELECT sno, cno, grade FROM ( SELECT sno, cno, grade, RANK() OVER (PARTITION BY cno ORDER BY grade DESC) AS rk FROM SC ) t WHERE t.rk <= 2;
很多教材没有写窗口函数,但如果老师在课堂上讲过,考试出现也不算超纲。当时我这道题空着,因为平时练的都是简单聚合,没有去了解窗口函数。如果你还没考试,建议把ROW_NUMBER、RANK、DENSE_RANK的区别记得很熟,尤其PARTITION BY和ORDER BY的位置不能反。窗口函数不是数据库系统原理教材的绝对重点,但它出现在阶段考里,说明现在的出题方向越来越贴近实际工程。
3. 选择题和填空题里的埋伏:范式、事务、索引的易混点
基础题看着简单,但如果没有把概念之间的边界理清,选择题几乎一做一个错。我把10月考试里反复出现的易混点整理成了三组,都是在草稿纸上推过的。
3.1 范式判断中的函数依赖游戏
范式题最常考的是给出一个关系模式,让你判断最高属于第几范式。我当时遇到的关系模式是R(学号, 姓名, 系名, 系主任),函数依赖为:学号→姓名,学号→系名,系名→系主任。主码是学号,非主属性有姓名、系名、系主任。学号能直接推出系名,而系名又能推出系主任,于是系主任对学号形成传递函数依赖,所以R最高只能达到2NF,而不是3NF。
正确的分解方式是拆成两个关系:R1(学号, 姓名, 系名),R2(系名, 系主任)。这样R1里学号是码,R2里系名是码,两个都已经满足BCNF。考试还会接着问这个分解是否无损连接、是否保持函数依赖。无损连接的判断标准是:两个分解模式的公共属性必须至少是其中一个模式的候选码。这里公共属性是系名,而系名是R2的候选码,因此无损。保持函数依赖则看每个函数依赖是否能在某个分解模式里直接推出,系名→系主任在R2中保留,学号→姓名和学号→系名在R1中保留,所以也保持。
这里有一个很常见的误解:有人认为2NF只需要消除部分函数依赖,所以只要没有非主属性对码的部分依赖,就一定是3NF。实际上,3NF还要消除非主属性对码的传递依赖。判断时先列出所有函数依赖,然后找出所有候选码,再看每个非主属性是“直接依赖”还是“传递依赖”。千万不能偷懒。
3.2 事务隔离级别与并发问题对照表
简答题考了事务ACID,选择题就考了对隔离级别的理解。四档隔离级别和可能出现的并发问题如下表:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 |
| 读已提交 | 不可能 | 可能 | 可能 |
| 可重复读 | 不可能 | 不可能 | 可能 |
| 可串行化 | 不可能 | 不可能 | 不可能 |
这里要特别提醒,这张表是SQL标准里的定义。实际数据库产品会有差异,比如MySQL InnoDB默认是可重复读,但它通过next-key lock在绝大多数场景下也避免了幻读,所以如果你做题时用的是“MySQL实测”去质疑教材,就可能被扣分。考试请以教材定义为准,先答标准定义,再在备注里写实际实现。我当时心理上很纠结,后来跟老师确认过,本科试卷就要按教材来。
隔离级别之外,并发控制还有个高频概念是“两阶段锁协议”。一个事务必须分两个阶段加锁:扩展阶段只能加锁不能解锁,收缩阶段只能解锁不能加锁。两阶段锁协议能保证冲突可串行化,但不保证不会死锁。死锁的解决通常靠超时或等待图,这也是填空爱出的点。理解两阶段锁的方式很简单——把它想象成“先囤货再卖货”,前期疯狂进货,后期只准清仓,不许补货。
3.3 索引选型与B+树的几个经典问法
选择题里关于索引的坑集中在“什么时候用B+树,什么时候用哈希”。哈希索引特别适合等值查询,比如“WHERE id = 123”,它可以在O(1)时间内定位;但如果是范围查询“WHERE age BETWEEN 20 AND 30”,哈希索引就无能为力了。B+树索引对范围查询非常友好,因为它的叶子节点通过指针连成了一个有序链表,从一个叶子出发可以顺序遍历后续所有叶子。这个点经常被单独拎出来作为填空:B+树可以高效支持范围查询的主要原因是“叶子节点之间用指针链接”。
还有一个高频选择题是聚簇索引和非聚簇索引的区别。一张表最多只能有一个聚簇索引,因为聚簇索引决定了表数据的物理存储顺序,就像字典正文按拼音排列,只能有一种物理顺序。非聚簇索引相当于书后面的偏旁部首索引,它单独维护一棵B+树,叶子存的是主键值,查到主键值后再回表找整行。如果查询需要的数据都在二级索引里,就可能用“覆盖索引”避免回表,这是加分项。考试一般只要求记住“一个表最多一个聚簇索引”“非聚簇索引需要回表定位”。
4. 复习路线与教材选择:从第六版到第七版,我踩过的坑
到了这个阶段,你应该发现“试卷笔记”不只是记录题目,更是一份复习路线图。教材选哪本、怎么配合实验课,直接决定复习效率。
4.1 王珊《数据库系统概论》第六版怎么用
这本书是国内很多高校的指定教材,优点是表述规范,很多简答题的原话就是书里的定义。复习时不要把整本书从头到尾读一遍,而是按试卷考点倒逼回去:关系代数、SQL、范式、ER图、事务控制,分别对应书里的第2、3、6、7、11章。第一轮先快速过章节,把概念性内容做成问答卡片,比如“什么是参照完整性”“什么是游标”;第二轮只看自己错题涉及的小节。
我最想提醒的一点是,这本书的课后习题要动手写,尤其第6章范式的算法题,光看书很容易产生“我会了”的幻觉。施伯乐《数据库系统教程》第三版是很多学校的考研指定教材,理论推导更严密,但如果你不是考研,不必从头啃,遇到看不懂的算法回到王珊版查互补即可。我当时就是因为两本教材来回切换,一度导致概念混乱,最后干脆以一本为主,另一本只当字典。
4.2 Silberschatz《数据库系统概念》第七版作为补充
《数据库系统概念》第七版是我复习时的补充材料。它在关系代数上的讲解非常细腻,尤其是除运算那节,配了很多例子,比中文本科教材更容易理解语义。第七版里关于事务的部分也讲得比较系统,对于理解隔离级别和锁协议很有帮助。不过它的章节结构跟国内教材不同,不适合逐页读,我更建议把它当“概念词典”使用:当王珊书上某个定义看不明白时,就去英文版对应的章节找更直观的解释。
另外,这套书的课后题网上有很多答案资源,适合自测。但我要提醒一点:不要以为做完课后题就等于掌握了试卷。本科考试的风格更贴近“知识点组合”,一道SQL题可能同时考多表连接、子查询、聚合、视图,课后题往往是单一知识点。所以补充材料的定位是帮你打通概念,真正提分还是靠做组合题。
4.3 实验课与理论考试的配合:把作业变成考点
很多高校都有一门数据库系统实验课,像深圳大学数据库系统实验一的内容,基本就是围绕MySQL建库、建表、插入数据、完成简单查询。千万不要把实验课只当成“完成任务”,因为实验里的每一个动作都可能变成考试题。实验一让你建选课库,考试就可能让你手写CREATE TABLE,包括主码、外码、CHECK约束的写法;实验里让你用UPDATE更新成绩,考试就可能在填空题里问UPDATE语句的WHERE条件为什么不能省略。
我当时吃过一个大亏:平时在Navicat里写SQL,软件自动补全了很多东西,比如表名、列名甚至关键字的大小写,但考试是手写SQL,没有任何提示。所以从10月这次考试之后,我给自己定了一条规矩:每个实验SQL都必须先在纸上手写一遍,再敲到电脑里执行,用报错反向纠正记忆。这个方法帮我后来在期末SQL大题上拿满了分。实验课另一个价值是让你看到ER图转成关系模式之后真实的表结构,这样在考试里画ER图时心里更有底。
5. 考后笔记整理法:同一套试卷,至少榨出三遍价值
很多人考完试就把试卷扔到一边,或者只看个分数就完事。我后来把2022年10月这张试卷做了三轮整理,每一轮都能挖出新东西。
5.1 第一遍:对照答案标失分点
考完当天,趁着记忆还热,我凭草图把整张试卷的题目回忆出来,和同学的答案比对,并拿到了老师的参考答案。第一遍整理最核心的动作,是给每道错题标注“失分原因”,而不是只写正确答案。我会用三个标签分类:A类是完全不会,B类是知识点会但思路错了,C类是懂了但粗心写错。按分数占比去统计,当时我C类错误占了三分之一,非常可惜。针对C类错误,我会在题目旁边单独用红笔写“下次要检查GROUP BY的列是否合法”,这类提醒比补知识点更有效。
如果你找不到参考答案,也没关系,可以找教材课后题或网上同类题来对照。重点是先把题目回忆补全,不然过三天再整理,结论就会失真。试卷笔记的第一版不是给别人看的,是让你以最快速度知道“扣掉的分数到底死在哪”。
5.2 第二遍:把每道错题扩展成专题卡片
第二遍整理时,我不再逐个题目抄答案,而是把有共性的错题归类成专题卡片。比如所有和ER图转换有关的题,不论选择题还是综合题,都归到一张“ER转关系模式”卡片上。卡片内容分四段:知识点、题目原文、易错点、同类题。同类题可以来自课后习题或网上找到的ER图例题,自己动手重新做一遍,并把步骤写在卡片下方。
这样做的原理很简单:单道错题是孤立的,容易忘记;而专题卡片把规则、题目、陷阱放在一起,会形成一个记忆单元。我光是范式分解就做了一张卡片,上面写了“2NF看部分依赖、3NF看传递依赖、BCNF看决定因素是不是码”,下面贴了那道系主任题和一道变形题。考试前一晚我只过这些卡片,不再翻整本书,效率高了很多。
5.3 第三遍:用回忆版试卷做限时模拟
两周之后,我开始把整理好的回忆版试卷当作模拟题用,定上100分钟的闹钟,在一张白纸上从头到尾写一遍。第一次模拟成绩大概只有六成多,因为经过两周遗忘,那些靠着短期记忆背下来的填空和SQL题开始现出原形,正好暴露真正的薄弱点。模拟完再对答案,凡是错第二次的题,就已经不是知识盲区而是思路障碍了,需要用更狠的方式去纠正。
比如关系代数除运算我第一次错是括号问题,第二次错是因为忘了对SC投影。我在那个题目旁边写了一句“R÷S之前,先看R的列是否除了公共属性还多了其他列”,然后立刻找了一道课后除运算题做一遍。等到期末前再模拟第三次,整张回忆版试卷基本能在80分钟内完成,正确率到了九成。这种“考后三轮整理”的方法,让我把一次阶段考的试卷真正吃透了,期末复习时几乎不用再报班或狂刷网课。
回顾整个过程,我最大的体会是:试卷笔记的价值不在于“收藏”,而在于“反刍”。2022年10月那次考试,我的卷面分数并不高,但正是因为它暴露了关系代数、SQL窗口函数、范式分解这几块弱项,我才在后来的复习中精准补漏。如果你手头也有一张考研、期中或期末的数据库系统原理试卷,别急着扔,按上面这三轮方法整理下来,收获可能会比重新读一遍书更大。最后再分享一个小技巧:整理专题卡片时,尽量用提问句式来写标题,比如“为什么1:N联系不能单独建表?”而不是“1:N联系转换规则”,因为人面对问题时大脑才会真正进入检索模式。