数据库原理课后题拆解:关系代数、SQL与范式分解实战验证
简介这份资源是钱学忠《数据库原理及技术》教材的配套习题答案面向高校计算机专业学生、备考数据库相关课程考试的学习者以及希望巩固数据库理论与实践基础的自学者。内容围绕数据库设计、SQL语言、关系数据库理论、数据库管理系统与数据库管理等核心章节展开可帮助读者在完成课后练习后对照答案查漏补缺理清E-R模型转换、关系代数演算、范式规范化等易错知识点。资源以rar压缩包形式提供整体约16.69MB文件总数与具体类型明细上游暂未提供下载后可按章节顺序逐题核对。目前已有693人学习下载适合作为课程复习与习题自测的辅助材料配合教材使用能更高效地掌握数据库设计与管理的关键方法。1. 数据库原理及技术钱学忠答案一份被低估的课后题拆解手册如果你正在上《数据库原理及技术》这门课用的恰好是钱学忠老师编的那本教材那你大概率经历过这个场景课后习题翻来覆去看了三遍关系代数那道题还是不知道从哪下手SQL 写出来跑不通范式分解更是像在做玄学推理。这份“答案”资源本质上就是针对该教材课后习题的一套完整参考解答覆盖关系模型、SQL 语句、范式理论、事务与并发控制等核心章节。它适合正在跟课、准备期末、或者考研复习数据库的读者用来对照自己的解题思路而不是拿来直接抄。我拿到这份资料后逐章翻了一遍下面把它的结构、用法和几个容易翻车的地方拆开讲。2. 这份答案到底覆盖了什么章节映射与题型拆解2.1 教材章节与答案的对应关系钱学忠这本《数据库原理及技术》的章节编排基本遵循经典数据库教材的脉络从绪论和数据模型起步经过关系代数、SQL、规范化理论再到事务管理、并发控制和恢复技术。这份答案资源按章节组织每一章对应教材课后习题的编号给出参考解答。我拿到手后第一件事是对目录。常见做法是先把教材的章节目录抄下来然后逐条比对答案文件的章节标题确认没有缺章或错位。实际比对下来覆盖情况大致如下教材章节答案覆盖情况题型分布绪论与数据模型完整概念简答、模型对比关系代数与关系演算完整代数表达式、元组/域演算转换SQL 基础完整建表、查询、视图、权限规范化理论完整函数依赖、范式判定、分解事务与并发控制完整调度分析、锁协议、隔离级别数据库恢复技术完整日志分析、检查点、redo/undo从题型分布看关系代数和范式分解这两块占的比重最大也恰恰是最容易出错的部分。SQL 部分的答案相对直观但有些题涉及嵌套查询和聚集函数的组合光看答案不一定能理解为什么这样写。2.2 不同题型的拆解思路概念简答题的答案我一般不会死记而是先看它用了哪些关键词。比如问“数据库三级模式结构是什么”答案里一定会出现外模式、概念模式、内模式这三个词以及两层映像。抓住这几个锚点用自己的话组织就行。关系代数题是重灾区。答案里给出的表达式往往很简洁但省略了推导过程。我的习惯是拿到答案后反向推先看结果是什么关系再看用了哪些运算选择、投影、连接、除最后验证每一步的输入输出是否对得上。除法运算尤其要小心很多答案直接给结果但中间步骤需要自己补。SQL 题的答案通常可以直接在数据库里跑。我一般会建一个测试库把教材里的示例表结构建好然后把答案里的 SQL 逐条执行看结果是否和题目要求一致。这一步能过滤掉不少印刷错误或笔误。范式分解题答案给的分解结果不一定唯一。这时候不能只看“对不对”还要看“好不好”——是否保持函数依赖、是否无损连接。答案里如果只给了一个分解方案最好自己再想想有没有其他等价方案。3. 怎么用这份答案复现解题过程从建库到验证3.1 搭建本地测试环境要验证 SQL 和关系代数相关的答案最直接的办法是在本地建一个数据库。MySQL 和 PostgreSQL 都行我一般用 MySQL因为教材里的示例大多兼容。安装好之后先建一个练习库-- 创建练习数据库 CREATE DATABASE db_practice DEFAULT CHARACTER SET utf8mb4; -- 切换到该数据库 USE db_practice; -- 创建教材中常见的学生-课程-选课三表结构 CREATE TABLE Student ( Sno CHAR(8) PRIMARY KEY, -- 学号 Sname VARCHAR(20) NOT NULL, -- 姓名 Sage INT, -- 年龄 Sdept VARCHAR(20) -- 所在系 ); CREATE TABLE Course ( Cno CHAR(4) PRIMARY KEY, -- 课程号 Cname VARCHAR(40) NOT NULL, -- 课程名 Ccredit INT -- 学分 ); CREATE TABLE SC ( Sno CHAR(8), -- 学号 Cno CHAR(4), -- 课程号 Grade INT, -- 成绩 PRIMARY KEY (Sno, Cno), FOREIGN KEY (Sno) REFERENCES Student(Sno), FOREIGN KEY (Cno) REFERENCES Course(Cno) );这段建表语句对应的是教材里最经典的三表模型。Student存学生基本信息Course存课程信息SC是选课关系主键是学号和课程号的组合。外键约束保证引用完整性。字符集用utf8mb4是为了避免中文乱码这个坑后面还会提到。建完表后插入几条测试数据INSERT INTO Student VALUES (20210001, 张三, 20, 计算机系), (20210002, 李四, 21, 数学系), (20210003, 王五, 19, 计算机系); INSERT INTO Course VALUES (C001, 数据库原理, 4), (C002, 数据结构, 3), (C003, 操作系统, 4); INSERT INTO SC VALUES (20210001, C001, 85), (20210001, C002, 90), (20210002, C001, 78), (20210003, C003, 92);有了这些数据就可以把答案里的 SQL 逐条跑一遍对照结果。3.2 关系代数答案的验证方法关系代数没法直接在数据库里跑但可以翻译成 SQL 来验证。比如答案里给出一个除法运算的结果你可以把它转成NOT EXISTS的双重否定形式来验证。举个例子题目要求“查询选修了全部课程的学生学号”关系代数里用除法表示为π_Sno,Cno(SC) ÷ π_Cno(Course)。翻译成 SQL 就是-- 查询选修了全部课程的学生 SELECT DISTINCT Sno FROM SC AS X WHERE NOT EXISTS ( SELECT * FROM Course AS C WHERE NOT EXISTS ( SELECT * FROM SC AS Y WHERE Y.Sno X.Sno AND Y.Cno C.Cno ) );逻辑是对于每个学生如果不存在一门课程是他没选的那他就选了全部课程。双重NOT EXISTS是除法运算的标准 SQL 翻译模式。跑一下结果如果和答案一致说明你的理解没问题。参数说明X是外层的学生选课记录C是课程表Y是内层用来匹配当前学生和当前课程的选课记录。三层嵌套的顺序不能乱乱了语义就变了。3.3 范式分解题的验证步骤范式分解的答案验证起来最麻烦因为分解结果不唯一。我的做法是分三步走第一步检查分解后的每个关系模式是否满足目标范式。比如答案说分解到 3NF那就逐个检查每个模式里有没有非主属性对码的传递依赖。第二步验证无损连接。用 Chase 算法或者直接检查分解是否包含原关系的候选码。如果分解后的某个模式包含了原关系的候选码那一定是无损的。第三步验证是否保持函数依赖。把原关系的函数依赖集投影到每个分解后的模式上取并集看是否和原依赖集等价。这三步走完基本能判断答案给的分解方案是否合理。如果答案只给了一个方案你可以自己再试一个不同的分解对比两个方案的优劣。4. 避坑与排查那些年我对照答案时翻过的车4.1 现象SQL 答案跑不通报语法错误原因教材里的 SQL 可能基于标准 SQL 或者某个特定数据库的方言直接拿到 MySQL 里跑不一定兼容。比如教材里可能用了FULL OUTER JOIN但 MySQL 不支持这个语法。解决先确认答案里的 SQL 是针对哪个数据库写的。如果是标准 SQL在 MySQL 里遇到不兼容的语法查一下对应的替代写法。FULL OUTER JOIN在 MySQL 里可以用LEFT JOIN UNION RIGHT JOIN来模拟。4.2 现象关系代数表达式的结果和预期不一致原因关系代数里属性的顺序和名称容易被忽略。投影运算π后面跟的属性列表顺序不同结果关系的列顺序就不同虽然集合语义上等价但对照答案时容易误判。解决对照答案时先看属性集合是否一致再看元组集合是否一致。顺序问题可以忽略但属性名和值的对应关系不能错。4.3 现象范式分解答案看不懂推导过程原因答案往往只给最终分解结果省略了中间步骤。尤其是涉及属性闭包计算和多属性依赖的题目跳步很严重。解决自己动手算一遍属性闭包。从给定的函数依赖集出发对每个属性集求闭包看它能推出哪些属性。这个过程虽然繁琐但能帮你真正理解分解的逻辑。我一般会在草稿纸上把每一步的闭包都写出来再和答案对照。4.4 现象中文乱码建表后插入数据变成问号原因数据库的字符集没有设置成utf8mb4或者连接字符集不对。解决建库时指定DEFAULT CHARACTER SET utf8mb4建连接时加上?useUnicodetruecharacterEncodingutf8。如果已经建了库可以用ALTER DATABASE db_practice CHARACTER SET utf8mb4;修改。4.5 现象并发控制题的调度分析结果和答案对不上原因可串行化调度的判定需要画优先图答案可能省略了图的绘制过程直接给结论。如果优先图里有一条环那就是不可串行化的但环的检测容易漏边。解决老老实实画优先图。把每个事务的操作按顺序列出来遇到冲突操作读写同一数据项且至少一个是写就画一条有向边。画完后检查有没有环。这一步没有捷径画图最可靠。5. 进阶用法把答案变成自己的题库5.1 用答案反向出题这份答案最大的价值不是“对答案”而是“反向出题”。我习惯把答案遮住只看题目自己先做一遍然后对照答案找差距。更进一步可以把答案里的 SQL 改一改条件自己出变体题。比如原题是“查询选修了数据库原理的学生”你可以改成“查询选修了数据库原理且成绩大于 80 的学生”然后自己写 SQL 验证。5.2 建立错题索引对照答案的过程中错题是宝贵的。我一般会建一个错题表记录题号、错误原因、正确思路和涉及的知识点。比如题号错误类型知识点正确思路3-5关系代数除法漏条件除法运算双重 NOT EXISTS 翻译4-2SQL 嵌套查询顺序错子查询先内层后外层5-8范式判定漏传递依赖3NF检查非主属性对码的依赖这张表考前翻一遍比重新做一遍题还管用。5.3 用脚本批量验证 SQL 答案如果答案里的 SQL 题很多手动一条条跑太慢。可以写个 Python 脚本把 SQL 语句从文件里读出来逐条执行并输出结果import pymysql # 连接数据库 conn pymysql.connect( hostlocalhost, userroot, passwordyour_password, databasedb_practice, charsetutf8mb4 ) cursor conn.cursor() # 从文件中读取 SQL 语句按分号分割 with open(sql_answers.sql, r, encodingutf-8) as f: sql_text f.read() statements [s.strip() for s in sql_text.split(;) if s.strip()] for i, stmt in enumerate(statements): try: cursor.execute(stmt) results cursor.fetchall() print(f第 {i1} 条执行成功返回 {len(results)} 行) for row in results: print(row) except Exception as e: print(f第 {i1} 条执行失败{e}) cursor.close() conn.close()这段脚本的逻辑很直接连接数据库读取 SQL 文件按分号拆分语句逐条执行。charsetutf8mb4保证中文不乱码。执行失败的语句会打印错误信息方便定位问题。参数方面host、user、password和database需要根据你的本地环境修改。从那以后我每次拿到类似的课后答案资源都会先建库、跑脚本、画优先图把能自动化的部分自动化把不能自动化的部分手动画图验证。这套流程走下来答案就不再是“别人的答案”而是变成了自己的知识。希望帮到你。本文还有配套的精品资源点击获取