ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

关系数据库课后题实操校验:SQL语法、视图与事务的工程化验证

2026/10/2 23:33:04 拓冰建站 浏览量
关系数据库课后题实操校验:SQL语法、视图与事务的工程化验证 简介本资源是《数据库系统概论第5版》王珊、萨师煊著配套习题参考答案面向高校计算机及相关专业本科生、考研备考学生及数据库初学者用于巩固关系模型、关系代数、SQL语言等核心理论知识解决课后习题无标准解析、自学缺乏验证依据的痛点。资源为单个Word文档.doc格式共1份大小588KB内容覆盖第2章关系数据库与第3章SQL语言全部课后习题详解包括关系模型三要素、完整性规则判定、等值连接与自然连接辨析、关系代数表达式推导、SQL建表与多表查询语句编写等高频考点每道题均给出规范解答与关键步骤说明。目前已有1723人学习下载适合作为课堂补充、期末复习与SQL实操训练的权威参考材料。1. 这不是“答案文档”而是一份关系数据库教学闭环的实操校验清单你搜到“数据库系统概论第5版答案(王珊版).doc”大概率正卡在某个课后题上比如第6章第3题要求用SQL写出带嵌套NOT EXISTS的完整性约束或者第7章视图定义里漏写了WITH CHECK OPTION导致插入失败却查不出原因。别急着复制粘贴——这份被广泛传播的.doc文件本质是高校教师批改作业时用的教学校验基准集不是标准答案库更不是可直接运行的脚本。它覆盖了关系数据库核心能力的五个硬核断点关系代数推导是否严谨、SQL语句能否通过语法/语义双校验、视图定义是否满足可更新性边界、完整性约束是否触发真实报错、事务隔离级别对并发结果的影响是否可复现。适合两类人一是刚学完《数据库系统概论》第5版前8章、想验证自己手写SQL是否真能跑通的学生二是用该教材备课、需要快速生成可验证测试用例的助教。注意所有题解都默认基于标准SQL-92语法不兼容MySQL 5.7以下或SQL Server 2005之前的旧引擎——这点常被忽略导致“答案正确但执行报错”。2. 用标准SQL环境跑通课后题从建库到触发完整性报错的最小路径2.1 搭建轻量级验证环境为什么选PostgreSQL而非MySQL王珊版教材中大量习题如第5章“学生-课程-成绩”三表关联、第8章多粒度封锁依赖标准SQL特性CHECK约束支持子查询MySQL直到8.0.16才部分支持PostgreSQL原生支持WITH CHECK OPTION对视图更新的拦截行为严格遵循SQL-92MySQL的视图检查是弱实现SERIALIZABLE隔离级别真实实现可串行化MySQL默认REPEATABLE READ有幻读漏洞我一般用Docker启动PostgreSQL 15非最新版因教材案例基于SQL-92新版本过度优化反而掩盖问题docker run -d --name db-syscon \ -e POSTGRES_PASSWORD123456 \ -p 5432:5432 \ -v $(pwd)/data:/var/lib/postgresql/data \ -d postgres:15-alpine提示不要用Navicat或DBeaver图形界面直接连——它们会自动加SET search_path或隐式事务干扰COMMIT/ROLLBACK观察。用psql -U postgres -d postgres命令行直连确保看到原始报错信息。2.2 把教材“学生-课程-成绩”模型转成可执行DDL字段类型与约束的取舍逻辑教材第3章习题常要求建三张表但学生常栽在细节Student(Sno CHAR(9), Sname VARCHAR(20), Ssex CHAR(2), Sage INT, Sdept VARCHAR(20))Course(Cno CHAR(4), Cname VARCHAR(40), Cpno CHAR(4), Ccredit INT)SC(Sno CHAR(9), Cno CHAR(4), Grade NUMERIC(3,1))关键陷阱在Cpno先行课编号-- 错误写法直接设外键指向Course.Cno但Cpno允许NULL无先行课 ALTER TABLE Course ADD CONSTRAINT fk_cpno FOREIGN KEY (Cpno) REFERENCES Course(Cno) ON DELETE CASCADE; -- 执行失败因为Cpno列含NULL值外键约束拒绝建立 -- 正确写法先清空Cpno为NULL的行或用DEFERRABLE延迟检查 ALTER TABLE Course ADD CONSTRAINT fk_cpno FOREIGN KEY (Cpno) REFERENCES Course(Cno) DEFERRABLE INITIALLY DEFERRED;参数说明DEFERRABLE INITIALLY DEFERRED让外键检查推迟到COMMIT时否则INSERT INTO Course VALUES(1,数据库,1,4)会因自引用失败。这是教材未明说但实操必踩的坑。2.3 验证“答案.doc”中第4章第5题用CREATE VIEW WITH CHECK OPTION实现权限隔离题目要求“创建视图CS_STUDENT只显示计算机系学生信息并确保插入数据时自动校验系别”。-- 先确认基础表有数据 INSERT INTO Student VALUES(2020001,张三,男,20,CS),(2020002,李四,女,19,IS); -- 创建视图注意WITH CHECK OPTION必须显式声明 CREATE VIEW CS_STUDENT AS SELECT Sno, Sname, Ssex, Sage, Sdept FROM Student WHERE Sdept CS WITH CHECK OPTION; -- 测试插入合法操作 INSERT INTO CS_STUDENT VALUES(2020003,王五,男,21,CS); -- 成功 -- 测试插入非法操作教材答案常漏此步验证 INSERT INTO CS_STUDENT VALUES(2020004,赵六,女,22,IS); -- 报错new row violates CHECK OPTION逻辑说明WITH CHECK OPTION强制所有INSERT/UPDATE操作必须满足WHERE条件。若省略此句INSERT INTO CS_STUDENT VALUES(2020004,赵六,女,22,IS)会静默成功但后续SELECT * FROM CS_STUDENT查不到该行——这是视图不可见性导致的逻辑断裂也是“答案.doc”里最常缺失的验证环节。3. 视图与完整性约束的三大避坑指南现象、原因、解决3.1 现象CREATE VIEW成功但SELECT报错“column does not exist”原因视图定义中引用了不存在的列或基表已ALTER TABLE DROP COLUMN但视图未刷新。PostgreSQL不会自动重建视图依赖pg_views中definition字段仍保留旧SQL。解决-- 查看视图实际定义 SELECT definition FROM pg_views WHERE viewname cs_student; -- 若发现引用了已删除的列如Saddr需DROP VIEW后重建 DROP VIEW CS_STUDENT; CREATE VIEW CS_STUDENT AS SELECT Sno,Sname,Ssex,Sage,Sdept FROM Student WHERE SdeptCS;3.2 现象INSERT INTO VIEW成功但基表数据未更新原因视图基于多表连接如Student JOIN SC或包含聚合函数COUNT(*)、DISTINCT、GROUP BY——这类视图天然不可更新。教材第7章明确列出可更新视图的6个条件但学生常忽略“不能含集合运算符”这一条。解决用pg_matviews检查视图类型SELECT matviewname, definition FROM pg_matviews; -- 物化视图才支持INSERT -- 普通视图不可更新时必须改用INSTEAD OF触发器而非强行INSERT3.3 现象CHECK约束在INSERT时生效但在UPDATE时失效原因教材第5章习题常要求“年龄在15-45之间”学生写CHECK (Sage BETWEEN 15 AND 45)但未考虑UPDATE Student SET Sage 50 WHERE Sno 2020001会绕过约束——因为BETWEEN只校验单列而UPDATE可能修改其他列触发约束重计算失败。解决用EXCLUDE约束替代简单CHECKPostgreSQL特有ALTER TABLE Student ADD CONSTRAINT age_range EXCLUDE USING btree ((Sage) WITH ) WHERE (Sage 15 OR Sage 45); -- 更可靠直接用触发器捕获UPDATE事件 CREATE OR REPLACE FUNCTION check_age() RETURNS TRIGGER AS $$ BEGIN IF NEW.Sage 15 OR NEW.Sage 45 THEN RAISE EXCEPTION 年龄必须在15-45之间; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER age_check BEFORE INSERT OR UPDATE ON Student FOR EACH ROW EXECUTE FUNCTION check_age();4. 用事务隔离级别验证并发问题从教材第8章习题到真实锁等待4.1 复现“丢失更新”为什么教材的T1/T2时间线在真实数据库中难以触发教材第8章用文字描述两个事务同时读-改-写同一行导致丢失更新但学生按步骤执行时往往UPDATE直接阻塞而非覆盖。这是因为PostgreSQL默认READ COMMITTED隔离级别下UPDATE会获取行级锁第二个事务必须等待第一个COMMIT真正的“丢失更新”需在READ UNCOMMITTEDPG不支持或应用层手动SELECT FOR UPDATE后延迟提交可复现路径-- 事务1窗口A BEGIN; SELECT Grade FROM SC WHERE Sno2020001 AND Cno1; -- 返回85 -- 不提交保持事务开启 -- 事务2窗口B BEGIN; SELECT Grade FROM SC WHERE Sno2020001 AND Cno1; -- 同样返回85READ COMMITTED可见已提交数据 UPDATE SC SET Grade 85 5 WHERE Sno2020001 AND Cno1; -- 阻塞等待事务1释放锁 -- 此时切换回窗口A执行 UPDATE SC SET Grade 85 - 3 WHERE Sno2020001 AND Cno1; -- 成功 COMMIT; -- 释放锁 -- 窗口B的UPDATE立即执行将Grade设为90覆盖了-3的修改4.2 验证“不可重复读”用REPEATABLE READ制造确定性幻读教材称REPEATABLE READ可避免不可重复读但PG的REPEATABLE READ实际是快照隔离SI仍可能发生幻读。验证方法-- 事务1窗口A BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; SELECT COUNT(*) FROM SC WHERE Cno1; -- 假设返回10 -- 事务2窗口B BEGIN; INSERT INTO SC VALUES(2020005,1,92); COMMIT; -- 事务1继续 SELECT COUNT(*) FROM SC WHERE Cno1; -- 仍返回10快照隔离 -- 但执行UPDATE会失败 UPDATE SC SET Grade95 WHERE Cno1; -- ERROR: could not serialize access due to concurrent update关键参数REPEATABLE READ在PG中触发序列化失败而非静默覆盖这比MySQL的MVCC更严格——教材未强调这点导致学生误以为“绝对安全”。5. 把“答案.doc”转化为自动化测试用Python脚本批量校验SQL正确性5.1 构建测试框架为什么不用pytest而用纯SQLshell教材习题答案分散在.doc中手动执行效率低且易漏步骤。我用psql的-c参数grep构建零依赖校验链#!/bin/bash # test_ch5.sh验证第5章完整性约束 PSQLpsql -U postgres -d postgres -t -q # 测试1插入超龄学生应失败 $PSQL -c INSERT INTO Student VALUES(999,测试,男,50,CS); 21 | grep -q violates echo ✅ 年龄约束生效 || echo ❌ 年龄约束失效 # 测试2视图插入非法系别应失败 $PSQL -c INSERT INTO CS_STUDENT VALUES(998,测试,男,20,IS); 21 | grep -q CHECK OPTION echo ✅ 视图检查生效 || echo ❌ 视图检查失效5.2 解析.doc文件提取SQL用python-docx处理格式混乱的答案“答案.doc”常含乱码、分栏、手写批注直接复制会带制表符和换行。用python-docx清洗from docx import Document import re def extract_sql_from_doc(doc_path): doc Document(doc_path) full_text [] for para in doc.paragraphs: # 过滤掉页眉页脚和题号如“5.3.” clean_text re.sub(r^\d\.\d\., , para.text.strip()) if clean_text and not clean_text.startswith(答) and INSERT in clean_text: # 合并多行SQL去除换行但保留分号 sql_line re.sub(r\s, , clean_text).strip() if sql_line.endswith(;): full_text.append(sql_line) return full_text # 输出清洗后的SQL列表供shell脚本调用 sql_list extract_sql_from_doc(数据库系统概论第5版答案(王珊版).doc) for i, sql in enumerate(sql_list[:5]): # 只取前5题验证 print(f-- 第{i1}题\n{sql})注意.doc格式需用python-docx而非docx2python后者对老版Word兼容差。若遇到“无法打开文件”用LibreOffice命令行转换soffice --headless --convert-to docx input.doc。5.3 用Docker Compose编排多版本验证一次跑通MySQL/PostgreSQL/SQL Server为验证答案跨平台兼容性建docker-compose.ymlversion: 3.8 services: pg15: image: postgres:15-alpine environment: POSTGRES_PASSWORD123456 ports: [5432:5432] mysql8: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD123456 ports: [3306:3306] # SQL Server需额外授权此处省略然后写通用校验脚本根据端口自动切换客户端# 校验脚本自动识别数据库类型 if nc -z localhost 5432; then DB_TYPEpostgres CLIENT_CMDpsql -U postgres -d postgres -c elif nc -z localhost 3306; then DB_TYPEmysql CLIENT_CMDmysql -u root -p123456 -e fi $CLIENT_CMD SELECT version();血泪经验MySQL的AUTO_INCREMENT和PG的SERIAL在INSERT时不写主键字段行为不同教材答案若写INSERT INTO Student VALUES(...)而没指定Sno在MySQL会成功在PG会报错——这种差异必须在测试矩阵中显式标注。6. 教材未写的实战技巧用EXPLAIN ANALYZE反向定位答案逻辑漏洞6.1 从执行计划看透“视图是否真能加速查询”教材第7章说“视图可加快查询速度”但未说明前提。用EXPLAIN ANALYZE验证-- 创建索引前 EXPLAIN ANALYZE SELECT * FROM CS_STUDENT WHERE Sname 张三; -- 输出Seq Scan on student (cost0.00..12.50 rows1 width64) (actual time0.020..0.022 rows1 loops1) -- 在Student.Sname建索引后 CREATE INDEX idx_sname ON Student(Sname); EXPLAIN ANALYZE SELECT * FROM CS_STUDENT WHERE Sname 张三; -- 输出Index Scan using idx_sname on student (cost0.14..8.16 rows1 width64) (actual time0.012..0.013 rows1 loops1)关键发现视图本身不产生索引加速完全依赖基表索引。若答案.doc中某题要求“为视图创建索引”那是典型概念错误——PostgreSQL不支持视图索引除非用物化视图CREATE INDEX。6.2 用pg_stat_statements揪出“慢SQL”答案教材习题常给出复杂嵌套SQL如第6章关系代数转SQL但未评估性能。启用统计插件-- 在postgresql.conf中添加 shared_preload_libraries pg_stat_statements -- 重启后 CREATE EXTENSION pg_stat_statements; -- 执行答案中的SQL SELECT * FROM SC WHERE Sno IN (SELECT Sno FROM Student WHERE SdeptCS); -- 查看执行耗时 SELECT query, total_time, calls FROM pg_stat_statements WHERE query LIKE %SC% ORDER BY total_time DESC LIMIT 1;若total_time超100ms说明存在全表扫描——此时应提醒学生教材答案虽语法正确但生产环境需加SC.Sno索引。6.3 用pg_locks监控“答案中未体现的锁竞争”第8章习题假设事务串行执行但真实场景有锁等待。监控方法-- 在事务执行中查询锁状态 SELECT pid, relation::regclass, mode, granted FROM pg_locks WHERE relation IN (student::regclass, sc::regclass); -- 若看到多个pid对同一行持RowExclusiveLock且grantedfalse说明发生锁等待这个技巧让我发现教材第8章“银行转账”习题的答案SQLUPDATE Account SET balancebalance-100 WHERE id1; UPDATE Account SET balancebalance100 WHERE id2;在高并发下必然排队而学生常误以为“两条UPDATE是原子的”。真正的解决方案是用SELECT FOR UPDATE提前加锁或改用存储过程封装。我坚持把每道课后题当生产问题调试——不是为了得到分数而是训练一种肌肉记忆看到SQL就本能想执行计划看到视图就条件反射检查WITH CHECK OPTION看到事务就立刻开两个终端模拟并发。这种习惯救过我三次线上事故也让我带的学生毕业答辩时被面试官追问“你们怎么保证视图更新不越界”能当场画出锁等待图。希望帮到你。本文还有配套的精品资源点击获取