简介:数据库原理课程配套的课后习题答案(第四版)PDF,面向正在学习SQL Server 2000、需要从实验角度理解数据库建库建表操作的在校生和自学者。文档以实验手册形式,系统讲解服务管理器、企业管理器、查询分析器等常用管理工具的使用方法,并区分Windows身份验证与SQL Server身份验证的适用场景。实验内容覆盖创建testdb、company等数据库,调整数据文件与日志文件参数,通过ALTER DATABASE添加文件组,以及用CREATE TABLE创建包含主键、外键、UNIQUE、CHECK约束的student、course、sc表,最后结合查询分析器实现SELECT数据检索。每个实验均配有目的、原理、设备、内容与示例,便于逐步跟练。整个资源包为单个PDF文件,大小约856KB,目前已有1829人浏览学习。读者借此可以夯实数据库理论基础,积累T-SQL实操经验,尤其适合期末复习或备战上机考试;文档中的语句和步骤也能快速对照练习,遇到疑问时可借助联机丛书检索关键字。
1. 数据库课后习题答案(第四版).pdf 该怎么读才不算白拿
拿到这份《数据库课后习题答案(第四版).pdf》,第一反应通常是翻到对应章节抄答案,但真正会读它的人,是拿它当一份「考点路线图」来用的。数据库原理这门课的课后题,密度最高的地方不在 SQL 语法本身,而在关系代数、范式分解、事务调度这几类需要判断和推导的题上。这些题背答案毫无意义,因为考试换一张表结构,答案就完全变了。
所以这篇内容按这样的思路展开:先把答案对应的理论边界说清楚,再讲怎么按可执行步骤复现和验证,最后把它延伸到课程设计和数据库面试场景。无论你是在校生、准备考研复试,还是打算转岗做数据开发,把这份 PDF 从「对答案」升级为「验结论」,才算真的用上了。
2. 关系模型与关系代数:课后题的第一类底层考点
2.1 先看答案前,自己把「关系的三要素」写出来
关系模型的基础题,阅卷时最看重的不是最终结果,而是你有没有把「关系」这个概念落在一个严谨的框架里。我一般建议拿到任何一道关系题,先在草稿纸上写三行:关系名、属性集合、候选码。这三样东西决定了后续选择、投影、连接操作能不能合法书写。
判断一道题要不要用关系代数而不是元组关系演算,基准是题目有没有显式给出「基于集合」的限定词。关系代数是集合操作,结果天然去重;元组演算是逻辑描述,允许变量和量词。课后题答案里这两种表达经常同时出现,但考试时按题目要求选一种即可,不要混写。常见错误是把「除」运算当成「差」运算——除运算的语义是「至少包含所有…」,差运算的语义是「排除…」,两者在自然语言描述中只差一个词。
2.1.1 三种完整性的判定顺序
关系完整性一般考三类:实体完整性、参照完整性、用户定义完整性。踩分点在于顺序——先判断主属性是否为空,再判断外码是否与主码对应,最后才讨论年龄、分数这类业务约束。很多答案只写「满足第二、第三类」,漏了主属性非空的前提,这种情况属于步骤缺失而不是结论错误。
2.2 五种基本运算的选择决定整道题的写法
关系代数的基本算子一共五个:选择、投影、并、差、笛卡尔积。连接、交、除都是派生运算。课后题答案里凡是出现自然连接的地方,标准改法会要求你解释成「笛卡尔积 + 选择 + 投影」。
这里有个高频失分点:先写选择再写投影,和先写投影再写选择,得到的关系模式可能不同。如果题目要求「查询年龄大于 20 岁的学生的学号和姓名」,正确顺序是π_{学号, 姓名}(σ_{年龄>20}(学生)),若先投影π_{学号, 姓名}(学生)再选年龄,年龄属性已被去掉,选择无法执行。这个顺序错误在答案校对时最容易暴露,拿这道题去对照 PDF 时,先看运算顺序再看结果集。
2.3 用一道最小示例题对答案
| 步骤 | 关系代数表达式 | 对应 SQL |
|---|---|---|
| 查询所有年龄大于20岁的学生 | σ_年龄>20(学生) | SELECT * FROM 学生 WHERE 年龄>20 |
| 只保留学号和姓名 | π_学号,姓名(学生) | SELECT DISTINCT 学号,姓名 FROM 学生 |
| 组合查询 | π_学号,姓名(σ_年龄>20(学生)) | SELECT DISTINCT 学号,姓名 FROM 学生 WHERE 年龄>20 |
-- 验证关系代数组合结果的等价 SQL SELECT DISTINCT 学号, 姓名 FROM 学生 WHERE 年龄 > 20;这段 SQL 中的DISTINCT对应关系代数的集合去重语义,WHERE对应选择算子,SELECT后的属性列表对应投影算子。需要注意:若学生表的主键是学号,同一学生不可能出现两条记录,此时DISTINCT不影响结果,写不写都对;但若查询属性不包含主键,比如只查姓名,就必须保留DISTINCT,否则 SQL 的默认包语义会返回重复行,与关系代数结果不一致。
对照 PDF 答案时,不要只看结果行数是否一致,还要检查输出的属性顺序。关系代数投影后属性的顺序由投影列表决定,SQL 同样遵循这一规则。如果答案属性顺序与你写的不同,通常是语义差别而非等价写法。
3. SQL 不是背出来的:用本地库把每道答案跑通
3.1 本地验证环境的最小搭建
课后题答案里的 SQL 不与具体数据库绑定,但验证时必须在真实环境里跑。我常用的方案是 SQLite 的单文件模式,适合验证查询逻辑;如果题目涉及存储过程、窗口函数或并发控制,则换用 MySQL 或 PostgreSQL 的本地实例。原因很简单:SQLite 默认支持标准 SQL 的大部分子集,零配置,一条命令就能启动。
sqlite3 textbook.db < init.sql这条命令从init.sql文件读取建表语句和数据插入语句,在textbook.db中完成初始化。sqlite3是 SQLite 的命令行客户端,textbook.db是数据库文件名,首次执行会自动创建该文件。每次想重置数据,删除textbook.db再执行一次即可。
-- init.sql 最小示例:建立学生与选课两表 CREATE TABLE 学生 ( 学号 TEXT PRIMARY KEY, 姓名 TEXT NOT NULL, 年龄 INTEGER ); CREATE TABLE 选课 ( 学号 TEXT, 课程号 TEXT, 成绩 NUMERIC, PRIMARY KEY (学号, 课程号), FOREIGN KEY (学号) REFERENCES 学生(学号) );建表语句里把学号设为TEXT而不是INTEGER,是因为学号常以 0 开头,整数类型会丢失前导零。FOREIGN KEY定义了参照完整性,插入不在学生表中的学号会直接报错,这本身就能验证课后题关于参照完整性的结论。
3.2 把一道「查所有选了数据库课的学生」的答案翻译成可执行 SQL
课本答案对这种题的常规写法是连接查询或嵌套子查询。最常见的实现是用IN子查询,因为它的思路和关系代数里的除法问题接近,但写法更直观。
SELECT 学号, 姓名 FROM 学生 WHERE 学号 IN ( SELECT 学号 FROM 选课 WHERE 课程号 = 'DB001' );子查询先找到所有选了 DB001 课程的学生学号,外层查询再把这些学号映射回学生姓名。这里有个容易忽略的细节:若选课表中同一学生对同一课程有多条记录,答案会重复;但学号已作为选课表联合主键的一部分,逻辑上不允许重复,因此无需担心。
对比 PDF 答案时,重点看它是否加了DISTINCT。如果课后题答案用「存在量词」表达等价语义,那么 SQL 标准写法就是EXISTS,性能通常好于IN,但这道题作为习题,两种写法都该能看懂。
-- EXISTS 等价写法 SELECT 学号, 姓名 FROM 学生 s WHERE EXISTS ( SELECT 1 FROM 选课 c WHERE c.学号 = s.学号 AND c.课程号 = 'DB001' );EXISTS是关联子查询,对每个外层学生检查是否存在一条满足条件的选课记录。这里不推荐用JOIN后加WHERE的方式替代,因为若一个学生选了多门课,连接结果会产生重复行,还需要额外去重,对答案核对增加干扰。
3.3 结果集校验点:答案对不等于语句对
跑通只是第一步,还需要核对三件事:返回列数、返回行数、列的顺序。课后答案里的结果通常以表格形式给出,行数是最直观的校验点。
另一个常被忽略的校验点是 NULL 的参与。若题目问「没选任何课程的学生」,答案必须用NOT IN或LEFT JOIN ... IS NULL,此时NOT IN子查询的返回集中一旦含有 NULL,整个查询结果会变成空集。这个陷阱在答案 PDF 里经常被一笔带过,但实际运行一次就会暴露。建议把这种边界情况作为附加验证动作,专门构造一行 NULL 数据插入选课表,再观察结果变化。
4. 范式与数据库设计:课后题最需要推导的部分
4.1 判范式先列函数依赖,不是先看图
范式题的标准流程不是画 ER 图,而是把所有函数依赖写成X -> Y的列表。每个属性集合 X 的闭包决定了它能推出哪些属性,闭包覆盖全部属性则 X 是超码。我见过的失分案例几乎都是因为跳过闭包计算直接凭直觉判断,导致把 2NF 误判成 3NF。
以「学生(学号, 姓名, 系号, 系主任, 课程号, 成绩)」为例,存在以下函数依赖:学号 -> 姓名、学号 -> 系号、系号 -> 系主任,(学号, 课程号) -> 成绩。候选码是 (学号, 课程号),系主任依赖于系号,而系号只是候选码的一部分,因此存在对候选码的部分依赖,该关系最高只达到 1NF。
4.1.1 求闭包的方法
设 X 为属性集合,重复执行「若 U -> V 在依赖集中且 U ⊆ X,则 X := X ∪ V」,直到 X 不再变化。闭包等于全部属性时,X 就是一个超码。答范式题时把这个过程写两行在试卷上,比直接写结果得分率高。
4.2 分解到 3NF 的保持依赖与无损连接怎么同时验证
3NF 分解的标准算法分两步:先求正则覆盖,再对每个依赖单独建表。很多课后答案只给分解结果,不说明是否无损,是否保持依赖,而这两个性质恰恰是得分点。
无损连接验证用 Chase 算法:把所有分解后的关系模式属性拼成一张表,反复套用函数依赖补值,若某一行填满则无损。这个算法最好别在考场现场推,平时用纸笔练熟即可。
| 验证项 | 方法 | 通过条件 |
|---|---|---|
| 无损连接 | Chase 算法 | 最终某行无空值 |
| 保持依赖 | 检查每个依赖是否在某个分解模式中成立 | 每个函数依赖至少被一个关系模式覆盖 |
-- 验证性查询:检查分解后表能否通过连接还原 SELECT COUNT(*) FROM 学生基础 JOIN 系信息 USING (系号) JOIN 成绩 USING (学号, 课程号);若查询结果与原始关系表的行数完全一致,说明该分解在数据层面满足无损连接。USING子句指定连接公共列,避免同名列产生两个连接条件。需要注意COUNT(*)受重复值影响,连接前先确认连接列上没有重复组合,否则需要加DISTINCT。
4.3 一张表讲透 1NF 到 BCNF 的判定边界
| 范式 | 核心条件 | 常见反例 |
|---|---|---|
| 1NF | 属性不可再分 | 一个字段存多个电话号码 |
| 2NF | 消除非主属性对码的部分依赖 | 学号+课程号做码,系名只依赖学号 |
| 3NF | 消除非主属性对码的传递依赖 | 学号 -> 系号 -> 系主任 |
| BCNF | 每个非平凡依赖的左侧都是超码 | 系号 -> 系主任,但系号不是超码 |
这道判断顺序在课后题里几乎是固定出现的:先拆到原子属性,再找候选码,再找部分依赖和传递依赖。数据库课程设计里的表结构设计也常用这组判定,能把 3NF 讲明白的人,设计的表基本不会在后续开发里频繁 ALTER TABLE。
实际建模时,不建议所有表都强行上 BCNF。如果属性之间依赖关系极简单,BCNF 和 3NF 没有差别;遇到多值依赖时,4NF 才会登场。课后习题答案到 BCNF 一般为止,更深的内容在后续章节才会涉及。
5. 事务、死锁与恢复:习题答案里藏着的并发考点
5.1 四个 ACID 性质在题目里通常被拆成什么问法
课后题很少直接问原子性是什么意思,而会用「一个事务执行到一半系统断电,数据会变成什么状态」来考。这类题的标准答案围绕日志展开:写入数据前,先写撤销日志(Undo Log),发生故障时根据日志回滚到事务开始前。理解这点后,答「断电恢复」的题目就不再是死记硬背,而是顺着日志顺序推导。
一致性是四个性质里最难在习题中直接验证的,因为业务约束通常由应用程序保证,数据库只保证事务前后的状态合法。做课后题时要注意区分:数据库保证的是一致性的实现机制(约束、触发器、原子性),而不是业务规则本身。
5.2 调度与隔离级别对照:可重复读到底防住了什么
并发控制的课后题集中在三类异常:脏读、不可重复读、幻读。标准答案会给出隔离级别与异常的对应关系,这个对应关系也是数据库面试题的高频出处。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 |
| 读已提交 | 避免 | 可能 | 可能 |
| 可重复读 | 避免 | 避免 | 可能 |
| 串行化 | 避免 | 避免 | 避免 |
MySQL 默认使用可重复读,但通过间隙锁在多数情况下也避免了幻读。课后题答案通常按 SQL 标准写,此时可重复读不防幻读,若考试用的是 MySQL 教材,则要单独标注实现差异。
-- 事务中验证可重复读的行为 BEGIN TRANSACTION; SELECT 成绩 FROM 选课 WHERE 学号 = '1001'; -- 此时若另一个事务提交了该学生的新成绩 -- 再次查询仍读到旧值,说明当前隔离级别下可重复读生效 SELECT 成绩 FROM 选课 WHERE 学号 = '1001'; COMMIT;这段 SQL 里两个相同的SELECT在可重复读隔离级别下返回相同结果,说明当前事务的读是基于一致快照的。若想观察不可重复读,把隔离级别改为READ COMMITTED再执行同样代码,第二次查询会读到其他事务已提交的新值。
5.3 日志与恢复:redo/undo 在答案里怎么体现
恢复技术题目常给出一段日志序列,要求判断哪些事务需要重做,哪些需要撤销。判断依据是检查点:检查点之前已提交的事务需要 Redo,未提交的事务需要 Undo。
# 查看 SQLite 是否开启 WAL 模式 sqlite3 textbook.db "PRAGMA journal_mode;"返回delete表示默认回滚日志模式,返回wal则说明启用了预写日志。WAL 模式对应 Redo 的持久化策略,数据先写 WAL 再落主库,崩溃时从 WAL 恢复。这个 PRAGMA 命令本身不修改任何数据,只用于查看当前配置,适合验证教材日志部分的结论。
数据库死锁在课后题里的标准问法是「给出预防和检测两种思路」。预防靠资源排序,检测靠等待图。答这类题时,画等待图比文字描述更有说服力:事务 T1 持锁 A 等 B,事务 T2 持锁 B 等 A,形成环即死锁。数据库课程设计里出现死锁时,先查数据库「事务正在等待的资源」这类视图,再反向推断业务里的加锁顺序,通常就是答案。
6. 把课后答案改造成一套数据库面试自测题
6.1 反转题目法:把「求结果」改成「找错误」
课后题多为「写出查询结果」,面试相反,会给你一段 SQL 问有什么问题。用 PDF 里的习题做变体训练时,可以刻意把题目中的数据改动几个值,让结果变化,再自己解释原因。这比重新刷题更能检验对概念的理解深度。
6.2 用答案文档建一个最小题库脚本
grep -E "^[0-9]+\." "数据库课后习题答案(第四版).pdf" | head -50这条命令用grep提取 PDF 里以数字和点号开头的行,通常是题号,配合head -50截取前 50 道题,形成一份快速自测清单。-E启用扩展正则,^[0-9]+\.匹配行首的数字加句点。PDF 的文本提取可能包含空格或特殊字符,若结果为空,说明该 PDF 是扫描版,需要先做 OCR 或改用其他方式提取。这个技巧适合把教材目录快速映射成复习计划,也可配合数据库课程设计报告整理知识点。
6.3 一个验答案的小技巧:随手建一张数据量很小的表
练习题不要直接导入教材现成的数据库文件,我一般用 SQLite 手打 3 到 5 行数据,专门挑容易出错的行,比如 NULL、重复值、空字符串。这道工序花不到两分钟,但验证效果比整套数据好得多:SQL 逻辑里对空值的处理差异只有在小数据上才会被精确观察到,大数据量的全表扫描反而把边界情况掩盖了。用 dbeaver 或 DataGrip 打开同一条 SQL 做对照,能顺带确认不同数据库对标准 SQL 的兼容程度。这种「最小样例验证法」在调试数据库同步工具、排查 join 语义时同样适用,验证一次,比看十页答案更管用。
本文还有配套的精品资源,点击获取