数据库原理课后习题答案:关系代数、SQL、范式与事务逐题对照

发布时间:2026/10/27 10:55:06
数据库原理课后习题答案:关系代数、SQL、范式与事务逐题对照 简介《数据库原理及技术》钱学忠配套习题答案是面向高校数据库课程学习者与备考人员的答案汇编对应教材中的概念理解、SQL 编写和关系模型分析等练习用于课后自查、期末复习与查漏补缺。内容围绕数据库设计中的需求分析、E-R 模型与关系模式转换SQL 查询与更新操作关系代数、关系演算及范式规范化以及 DBMS 的并发控制、恢复与权限管理等章节展开可帮助读者对照题目梳理解题思路理解规范化设计与查询优化的判断依据。压缩包约 16.69MB文件总数与类型明细暂未提供下载后可解压查看具体内容。目前已有 693 人学习适合需要同步练习、核对答案或准备课程考试的学习者使用。1. 对答案对到怀疑自己问题多半不在你带课程设计那阵子被问得最多的一句话是数据库原理及技术的课后习题我写完不知道对不对网上搜到的答案还互相打架。这份配套答案资源解决的就是这件事——它按章节给出关系代数、SQL 语句、函数依赖与范式分解、事务调度与恢复的参考解覆盖从关系模型到并发控制的整条主线。适合正在跟这门课、写完题却没人批改的人也适合隔了几年回头补基础、想快速验证自己判断的在职开发者。真正费时间的从来不是把题做出来而是做完之后没法确认对错一份能逐题对照的答案省下的是反复自我怀疑的时间。下面按「题型怎么分布 → SQL 怎么复现 → 范式事务怎么判 → 哪些地方必翻车」的顺序拆开讲。2. 先摸清题型地图六类题各自的权重和先后顺序拿到一份答案直接从头翻是最低效的用法。教材习题的题型分布差异很大概念题占分不多但耗时最长SQL 题看着简单但答案写法不唯一范式题和事务题一旦理解偏了后面整章都会连环错。先把题型分清再决定哪类题值得逐字对照答案、哪类题只看思路即可。2.1 六类题型的依赖关系决定刷题顺序按常见教材的组织方式习题大体落在六类上概念与简答、关系代数、SQL、ER 模型与数据库设计、函数依赖与范式、事务并发与恢复。它们之间存在明确的先后依赖前一类没弄透后一类只能靠背。题型依赖的前置答案该怎么用建议投入概念简答无只对关键词不对表述低关系代数关系模型基础逐个符号核对尤其是除运算中SQL关系代数必须本地跑通答案只作对照高ER 设计与转换关系模型对实体、联系、基数三处中函数依赖与范式关系代数基础对推导过程不对最终分解高事务与恢复并发基础概念对判定结论调度图自己画一遍高依赖关系里最关键的一条是关系代数到 SQL 是单向的。SQL 写不出来八成是关系代数那一步没想清楚「先选后连还是先连后选」。反过来SQL 写对了但关系代数写错说明你只是记住了模板。所以碰到 SQL 题反复出错别急着刷更多 SQL回头把对应章节的关系代数题重做一遍效率高得多。另一条是函数依赖到范式的单向依赖。候选码求错后面 2NF、3NF、BCNF 的判定全错而且错得很隐蔽——分解出来的关系看起来「挺合理」实际上候选码就漏了属性。这类题必须按固定流程走凭感觉判断迟早翻车。事务那一块相对独立但它和「数据库设计」共用一套思维方式都是先列出所有约束再验证是否满足。调度可串行化判定就是画优先图看有没有环和范式判定里验证无损连接是同一类动作。2.2 用题号索引表把答案和教材对齐答案资源最大的问题是页码和教材对不上尤其是不同印次。稳妥的做法是自己建一张索引表四列就够题号、教材小节、知识点标签、我的错因。前两列照着教材抄后两列边做边填。具体操作按这四步走先翻教材目录把每一章的习题页码范围记下来形成「章 → 题号区间」的粗映射。做题时给每道题打一个知识点标签标签控制在十个以内比如「SQL-连接」「范式-候选码」「事务-隔离级别」标签太细反而没法统计。错题只记错因不抄整道题。错因写一句人话比如「NOT IN 遇到 NULL 返回空」比抄一遍题干有用。每做完一章把这一章的标签统计一次错得最多的标签就是下一章开始前要回看的小节。这套动作的价值在于它把「刷题」变成了「定位」。答案本身只是参照物真正让你进步的是那张错因表——它告诉你知识缺口在哪个标签上而不是笼统的「我这章学得不好」。2.3 一份可以直接照抄的三轮节奏时间紧的时候不要按章节平均用力按轮次推进更快。第一轮只做关系代数和 SQL目标是能独立写出正确语句答案只用来对最终结果第二轮做范式与 ER 设计目标是推导过程能自圆其说答案用来对中间步骤第三轮做事务与恢复目标是判定结论正确答案用来对优先图和日志顺序。每一轮的时长建议按 4:3:3 分配第一轮最长因为关系代数和 SQL 是后面所有内容的地基。第一轮里如果某道 SQL 题你想了十五分钟还没思路直接看答案的思路部分然后合上答案自己重写一遍——注意是重写不是抄写。抄写一遍带来的熟悉感是假的合上答案重写才是真的。三轮结束之后再回头把错因表里出现两次以上的标签集中处理一遍。这类反复出错的点通常只有三五个处理掉它们整门课的题感会明显不一样。3. SQL 题别只看答案本地建库复现的完整链路SQL 题的参考答案有个天然缺陷同一道题可能有五六种写法答案给的往往是最「教科书」的那一种未必是最容易理解的。更麻烦的是答案里的语句对错你根本看不出来——少一个 GROUP BY、多一个 JOIN 条件肉眼很难发现。唯一可靠的办法是把教材里的示例表在本地建起来把答案敲进去跑一遍看结果集和预期是否一致。3.1 建库建表把答案里的约束逐条落地教材里的表结构描述通常是文字加表格落到 SQL 上要做三件事确定主键也就是候选码里选出来的那个、确定外键和参照完整性、把值域约束写成 CHECK。以最常见的「学生-课程-选课」三表为例-- 本地复现用的库字符集按中文环境选 CREATE DATABASE IF NOT EXISTS db_course DEFAULT CHARACTER SET utf8mb4; USE db_course; CREATE TABLE student ( sno CHAR(8) PRIMARY KEY, -- 学号教材里写的候选码 sname VARCHAR(20) NOT NULL, ssex CHAR(2) CHECK (ssex IN (男,女)), sage TINYINT CHECK (sage BETWEEN 15 AND 45), sdept VARCHAR(20) ); CREATE TABLE course ( cno CHAR(4) PRIMARY KEY, cname VARCHAR(40) NOT NULL UNIQUE, credit TINYINT CHECK (credit BETWEEN 1 AND 10), cpno CHAR(4), -- 先修课自引用外键 CONSTRAINT fk_course_cpno FOREIGN KEY (cpno) REFERENCES course(cno) ); CREATE TABLE sc ( sno CHAR(8), cno CHAR(4), grade DECIMAL(5,1) CHECK (grade BETWEEN 0 AND 100), PRIMARY KEY (sno, cno), -- 复合主键对应教材里的候选码组合 CONSTRAINT fk_sc_sno FOREIGN KEY (sno) REFERENCES student(sno) ON DELETE CASCADE, CONSTRAINT fk_sc_cno FOREIGN KEY (cno) REFERENCES course(cno) );几个参数要说明白。CHAR(8)和CHAR(4)是定长学号课程号这类编码用定长比变长查询更快也避免前后空格带来的比较问题。DECIMAL(5,1)而不是FLOAT是因为成绩要精确比较浮点数在grade 90这种条件上会出现匹配不到的情况。ON DELETE CASCADE只加在选课表对学生的引用上删学生连带删选课记录符合业务直觉课程那边不加是因为删课程不该顺手抹掉历史成绩。执行顺序也有讲究。course表有自引用外键插入数据时先修课必须先存在否则直接报 1452 外键约束错误。教材答案里通常不写数据插入顺序这是复现时第一个卡点。3.2 多表连接与子查询用两三种写法互相验证SQL 答案不唯一验证思路就是用不同写法跑同一道题结果集一致才说明你真的懂了。最典型的是「查询选修了全部课程的学生」——关系代数里的除运算SQL 里对应双重 NOT EXISTS-- 写法一双重 NOT EXISTS对应关系代数的除运算 SELECT s.sname FROM student s WHERE NOT EXISTS ( SELECT 1 FROM course c WHERE NOT EXISTS ( SELECT 1 FROM sc WHERE sc.sno s.sno AND sc.cno c.cno ) );这四层嵌套读起来绕逻辑其实很直白对每个学生检查「是否存在一门课他没选」如果不存在就入选。内层SELECT 1不关心查出什么只关心有没有行写成SELECT *也对但1更省事。同一道题用聚合写法验证-- 写法二计数比对用总课程数做基准 SELECT s.sname FROM student s JOIN sc ON sc.sno s.sno GROUP BY s.sno, s.sname HAVING COUNT(DISTINCT sc.cno) (SELECT COUNT(*) FROM course);两种写法结果必须一致。不一致的时候别急着改 SQL先看数据sc表里有没有重复的选课记录course表里有没有课程一条选课记录都没有。这两种数据状况会让写法二和写法一出现分歧教材上的示例数据往往很干净自己造数据时就容易踩到。还有一种常见错误是拿NOT IN去替代NOT EXISTS。这两者在子查询结果含 NULL 时行为完全不同NOT IN会整体返回空集而NOT EXISTS正常返回。这个坑后面单独讲。3.3 视图、索引、权限三类容易被跳过的题教材后半部分常出现视图、索引、权限的习题很多人直接对着答案抄一遍就过。这三类恰恰是工作中最常用的值得花十分钟跑通。-- 视图把三表连接固化注意 LEFT JOIN 保证没选课的学生也在结果里 CREATE VIEW v_student_sc AS SELECT s.sno, s.sname, c.cname, sc.grade FROM student s LEFT JOIN sc ON sc.sno s.sno LEFT JOIN course c ON sc.cno c.cno; -- 索引外键列和经常出现在 WHERE 里的列优先建 CREATE INDEX idx_sc_cno ON sc(cno); CREATE INDEX idx_student_dept ON student(sdept); -- 权限只授视图的查询权不碰基表 CREATE USER u_readlocalhost IDENTIFIED BY Read_2024; GRANT SELECT ON db_course.v_student_sc TO u_readlocalhost;视图这里的关键是LEFT JOIN的顺序学生左连接选课没选课的学生会保留一行grade为 NULL。如果写成内连接视图就丢人教材答案里经常用内连接是因为它的示例数据里每个学生都选了课——这个隐含前提一旦被打破答案就不成立了。索引部分要注意索引不是越多越好。sc表上已经有(sno, cno)的复合主键索引最左前缀能覆盖按sno查的场景所以只需要再补一个cno单列索引给「按课程查学生」用。教材答案里有时会给sno单独建一个索引那是冗余的跑了 EXPLAIN 就能看出来。4. 范式与事务两类最容易「答案对、理解错」的题这两块的共同点是答案给你一个结论你不验证也能抄对但换个题面立刻不会。范式题的结论是一组分解后的关系模式事务题的结论是一个「是/否可串行化」的判断两者的正确性都依赖中间推导过程而答案通常只写结论。4.1 候选码怎么求四类属性分类法求候选码的流程是固定的别凭直觉。先把所有属性按在函数依赖里的出现位置分成四类属性类别定义处理方式L 类只出现在依赖左边一定属于某个候选码R 类只出现在依赖右边一定不属于任何候选码LR 类两边都出现可能属于需要试N 类两边都不出现一定属于每个候选码L 类和 N 类合起来先求闭包如果闭包等于全部属性那它就是一个候选码结束如果不等于就往里逐个加 LR 类属性再求闭包直到覆盖全属性。R 类属性直接排除。举个具体例子。设 R(A, B, C, D)函数依赖集 F {A→B, B→C, C→D}。A 只在左边属 L 类D 只在右边属 R 类B、C 两边都有属 LR 类没有 N 类。先算 A 的闭包A→B→C→D闭包是 ABCD等于全集所以 A 是唯一候选码。换个依赖集F {AB→C, C→D, D→A}。这里 A、B 出现在左边但在右边没出现A 在 D→A 右边出现过所以 A 是 LRB 只在左边是 L 类C、D 都是 LR。先算 B 的闭包只有 B 自己不够逐个加属性加 AAB 的闭包是 ABCD覆盖全集AB 是候选码加 CBC→A由 D→A 和 C→D 推得BCA 再推 D覆盖全集BC 也是候选码加 DD→ADA→B覆盖全集BD 也是候选码。所以候选码有三个AB、BC、BD。这一步最容易翻车的地方是漏掉 N 类属性。N 类属性不出现在任何依赖里意味着它无法被其他属性推出只能由自己提供所以必须出现在每个候选码中。教材答案里偶尔会漏自己推的时候一定要把 N 类单独标出来。4.2 无损连接与保持依赖两个判据别混用分解是否「好」教材给了两个独立判据无损连接和保持函数依赖。这两个判据是并列关系不是替代关系。很多人把无损连接当成唯一标准结果分解方案看着能还原原表但函数依赖全丢了答完还觉得自己做对了。无损连接判定两个子关系的情况有个直接定理R 分解成 R1 和 R2如果 R1∩R2 → R1−R2 或 R1∩R2 → R2−R1 成立则分解是无损的。也就是说公共属性能决定其中一个子关系的全部属性即可。保持依赖的判定更麻烦一些标准做法是把 F 投影到每个子关系上得到 Fi然后看这些 Fi 的并集能不能推出原来的每一条依赖。能推出就是保持的推不出就不保持。来看一个经典的两难R(A, B, C)F {AB→C, C→A}。分解成 R1(A, C) 和 R2(B, C)。公共属性是 CC→A 成立A 在 R1 里所以是无损的。但 AB→C 这条依赖在 R1 里推不出R1 只有 C→A在 R2 里也推不出R2 没有任何非平凡依赖所以不保持依赖。无损但不保持——这个结论必须两个判据都跑一遍才能得到。三个及以上子关系的无损判定要用表格法Chase 算法画一张 k 行 n 列的表第 i 行对应第 i 个子关系列对应全部属性子关系里有的属性填 aj没有的填 bij然后按函数依赖反复修改直到出现一行全是 a或者表不再变化。这个过程手算容易乱建议用铅笔按依赖一条一条改改完一条检查一次。4.3 事务与隔离级别把书面答案跑成可观察现象事务题的参考答案一般是文字描述某调度是否冲突可串行化、某隔离级别下会不会出现脏读。这些结论光看答案记不住开两个会话跑一遍就忘不掉了。-- 会话 A设为读已提交开启事务 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; START TRANSACTION; SELECT balance FROM account WHERE id 1; -- 第一次读假设得到 1000-- 会话 B另一个连接里改数据并提交 START TRANSACTION; UPDATE account SET balance 800 WHERE id 1; COMMIT;-- 回到会话 A再读一次 SELECT balance FROM account WHERE id 1; -- 读已提交下得到 800 COMMIT;同样的流程把隔离级别换成REPEATABLE READ第二次读到的还是 1000这就是可重复读和读已提交的差别。想观察幻读把操作换成范围查询加插入会话 A 先SELECT COUNT(*) FROM sc WHERE grade 90会话 B 插入一条 95 分的记录并提交会话 A 再查一次读已提交下数量变了可重复读下不变。参数上要注意SET SESSION和SET GLOBAL的区别前者只影响当前连接实验时用 SESSION别改全局不然会影响同实例上的其他库。另外 MySQL 默认隔离级别是 REPEATABLE READ如果你不显式设置第一次实验看到的就不是教材上说的读已提交行为很容易误判成「答案错了」。调度可串行化的判定则是画优先图调度里每对冲突操作不同事务、同一数据、至少一个是写画一条有向边图无环就是冲突可串行化。教材答案给的是「是/否」但考试和面试要的是过程边画漏一条结论就反了。常见的漏边是读-写冲突很多人只记得写-写冲突。5. 避坑排查五条最容易翻车的地方这些坑的共同特征是本地一跑就报错或者结果不对但对着答案看又找不出问题在哪。按「现象 → 原因 → 解决」记下来下次遇到直接查表。5.1 SQL 与建库环节的三条现象一NOT IN子查询返回空结果明明数据是存在的。原因是子查询结果集里含 NULLNOT IN的语义展开成x v1 AND x v2 AND ...其中任一项和 NULL 比较结果是 UNKNOWN整个条件不为真所以一行都返回不了。sc表里成绩为 NULL 的记录选了课没出分就是最常见的触发源。解决办法有两个改成NOT EXISTS相关子查询或者在NOT IN的子查询里加WHERE grade IS NOT NULL把 NULL 排掉。前者更稳后者要记得每处都加。现象二建表时外键约束创建失败报 1215 或 1452。1215 是父表不存在或父表对应列没有索引通常发生在建表顺序不对——先建了sc再建student。1452 是插入数据时引用的父记录不存在最典型的是course表的自引用外键先修课那一行还没插进去引用它的行就先插了。解决办法是调整建表和插入顺序父表先于子表自引用表按先修关系从底层往上插。实在要临时绕过可以SET FOREIGN_KEY_CHECKS 0但导完数据必须记得改回 1不然约束等于没建。现象三GROUP BY查询报 1055提示某列不在 GROUP BY 中。这是ONLY_FULL_GROUP_BY模式在起作用要求 SELECT 列表里的非聚合列必须出现在 GROUP BY 里。教材答案里常见的写法是SELECT sno, sname, AVG(grade) ... GROUP BY sno在旧版本能跑在新版本直接报错。原因是sname没有函数依赖于sno从 SQL 解析器的角度看不出来。解决办法是把sname也加进 GROUP BY或者用ANY_VALUE(sname)包一层再或者确认sno是主键后改写为子查询先聚合再连接。推荐最后一种语义最清楚。5.2 概念判定环节的两条现象四范式分解题答案看着对但一验证发现函数依赖丢了。原因是只验证了无损连接没验证保持依赖。这两个判据相互独立一个分解可以无损但不保持依赖也可以保持依赖但有损。做题时把两个判据都跑一遍形成固定动作。具体的验证方法在 4.2 节写过投影到子关系再取并集推原依赖推不出来就是不保持。现象五候选码求出来只有一个但答案给了三个。大概率是漏了 LR 类属性的组合。L 类和 N 类合起来闭包覆盖全集时只得到一个候选码但 LR 类属性两两组合、三三组合也可能是候选码必须逐个试闭包。另一种可能是把某个 R 类属性误当成了 LR 类——比如属性只在依赖右边出现却被算进了候选码导致多算出错误的组合。解决办法是分类那一步慢一点把每个属性在依赖集里出现的位置一个个标出来标完再动手算。6. 拿答案反向出题一套三十分钟的自测流程把答案从头到尾对一遍只能验证「这道题我做过」。真正能验证掌握程度的是拿答案反向出题把答案里的结论遮住只看题干限时写出结果再对照。这个过程我一般控制在三十分钟分三段。第一段十分钟专挑错因表里出现两次以上的标签每个标签从答案里找两道同类题遮住答案重做。做对了划掉做错了在错因后面加一个正字笔画——同一道题错三次以上说明不是粗心是这个知识点真的没通得回教材重读那一节。第二段十分钟做交叉验证。SQL 题用两种写法各跑一遍看结果是否一致范式题把分解后的关系模式代回原数据看能不能还原事务题把调度画成优先图看有没有环。这一步的目的是把「结论正确」升级成「过程自洽」。第三段十分钟把当次的错题按知识点聚类输出下一轮的复习顺序。这一步可以交给脚本省得手算# wrong_book.py —— 错题按知识点聚类错得多的排前面 from collections import Counter # 每条记录(题号, 知识点标签, 一句话错因) records [ (2-3, 关系代数, 除运算的选取顺序写反), (3-7, SQL, NOT IN 子查询含 NULL), (3-9, SQL, LEFT JOIN 条件写进 WHERE 变成内连接), (4-2, 范式, 候选码漏掉 N 类属性), (4-5, 范式, 只验无损没验保持依赖), (5-1, 事务, 隔离级别默认值记错), (5-4, 事务, 优先图漏画读-写冲突边), ] counter Counter(tag for _, tag, _ in records) # 次数相同时按基础依赖顺序兜底SQL 和范式优先 fallback [SQL, 范式, 事务, 关系代数] for tag in sorted(counter, keylambda t: (-counter[t], fallback.index(t))): nums [q for q, g, _ in records if g tag] print(f{tag}{counter[tag]} 题{, .join(nums)})这段脚本的逻辑很简单Counter统计每个标签出现的次数sorted的 key 用负数次数实现降序次数相同时用fallback列表里的位置决定先后。fallback的顺序不是随便排的SQL 和范式是后面所有内容的地基错在这两块要优先处理。运行之后输出的就是下一轮的复习清单直接照着做。有一点要提醒这个脚本只统计「记录下来的错题」不记录的题等于没做。我早期的习惯是只在脑子里记「这题我错了」结果一周后完全想不起来错在哪。后来改成做完一章立刻录进列表哪怕只写三个字也比事后回忆强。错题本的价值不在数量在于它能不能让你在考前半小时内定位到最该看的那五道题。从那以后我每次带人过这门课都强制走一遍「遮答案重做 交叉验证 聚类排期」这三段哪怕时间只够做第一段。答案资源本身只是参照物把它用成自测工具才算真的把这份材料吃透。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询