数据库课后实验全流程指南:从建表到事务避坑
发布时间:2026/10/9 13:38:49 锦皓数字建站

简介数据库课后实验崔巍编著是一份面向高校学生与数据库初学者的配套练习资源旨在帮助读者将关系数据库理论转化为实际操作能力重点训练SQL语法、数据查询与基础管理技能适合课程实验、期末上机或自学阶段使用。资源包含5个文件全部为SQL脚本压缩包仅5KB内容精简可直接在SQL Server、MySQL等数据库工具中打开运行。脚本按典型课后实验组织覆盖建表与数据类型定义、INSERT/UPDATE数据操作、SELECT条件查询、多表JOIN连接、GROUP BY分组聚合等核心知识点部分脚本还可配合事务处理、范式化设计等章节进行验证帮助理解ACID属性、数据一致性与规范化思想。已有220人浏览学习适合课后复习、实验补做或考前集中刷题通过运行、修改和对比这些SQL示例能够快速掌握数据库操作的常见写法并直观看到不同查询条件对结果的影响进而巩固课堂所学、提高实战能力。1. 数据库课后实验是什么把课堂例题变成能跑通的上机流程数据库课后实验是很多初学者栽跟头的地方——它不是把课堂上几个 SQL 例句跑一遍而是要把建库、查询、设计、事务串成一个完整上机流程。以某本主流数据库实验教材为例课后实验通常覆盖建表、增删改查、视图索引、ER 图转关系模式、范式分解和事务并发这几大块每一块都直接对应实验报告里必须交的结果截图。下面按做实验的顺序来先立环境再拆每类实验的套路最后把最容易翻车的地方提前踩一遍。适合两类人刚开数据库课、对着实验要求不知道第一步干什么的新手以及做完了但不确定对不对、想补一份自查清单的熟手。2. 先把实验环境立起来选库、建表与第一组必跑的 SQL课后实验的第一个门槛往往不是 SQL 本身而是「我这道题到底在哪个环境里跑」。实验指导书可能写的是某一种数据库的方言你电脑上装的是另一种还有同学的机器上是第三种。别慌这题能解。2.1 实验数据库怎么选跟着教材方言还是换个顺手的大多数数据库实验教材的示例代码是按某一种方言写的最常见的是 SQL Server 的 T-SQL 和 MySQL 两种。我的建议是优先用教材指定的方言去理解题意但本地实验环境用 MySQL 8.x 完全够用。原因是绝大多数课后实验的考察点是关系模型本身建表、查询、视图、约束这些语法两边几乎一致真正有方言差异的只有少数几个点。先把差异记住实验就不会卡在环境上。功能点T-SQL (SQL Server)MySQL取当前时间GETDATE()NOW()自增列IDENTITY(1,1)AUTO_INCREMENT截断结果集SELECT TOP 5LIMIT 5字符串拼接 号CONCAT()分页OFFSET...FETCHLIMIT 偏移量, 条数如果实验题明确要求用 SQL Server 完成而你又不想装完整版常见做法是装一个精简实例或者直接用教材配套的示例库文件。这里有一个关键选择交上去的实验报告里不能把两套方言混着写验收时要求 SQL 能在指导书指定的环境里原样运行。所以我的习惯是「读题用教材方言验证用本地环境」报告里只保留能在目标环境跑通的版本。2.2 建库建表的最小实验模板数据库课后实验第一题几乎都是「建立数据库 X并建立以下三张表」。这个模板直接抄能覆盖八成需求。-- 建库实验统一放独立 schema避免和已有数据混在一起 CREATE DATABASE IF NOT EXISTS lab_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_0900_ai_ci; USE lab_db; -- 建表主键、非空、默认值三件套一次写齐 CREATE TABLE student ( sno CHAR(9) NOT NULL, sname VARCHAR(20) NOT NULL, ssex CHAR(2) DEFAULT 男, sage SMALLINT, sdept VARCHAR(20), PRIMARY KEY (sno) ); CREATE TABLE course ( cno CHAR(4) NOT NULL, cname VARCHAR(40) NOT NULL, credit SMALLINT DEFAULT 0, PRIMARY KEY (cno) ); -- 中间表两个外键合起来做主键是选课类实验的标准写法 CREATE TABLE sc ( sno CHAR(9) NOT NULL, cno CHAR(4) NOT NULL, grade SMALLINT, PRIMARY KEY (sno, cno), FOREIGN KEY (sno) REFERENCES student(sno), FOREIGN KEY (cno) REFERENCES course(cno) );这个模板里三个容易出问题的地方。第一sno 用 CHAR(9) 而不是 INT是因为学号、工号这类编号是「编号」不是「数字」用数字类型保存会丢掉前导零实验题经常故意考这一点。第二中间表 sc 的主键是 (sno, cno) 联合主键这是多对多关系的标准落法漏掉联合主键会导致同一条选课记录被插入两次破坏数据的唯一性。第三外键约束建好后插入顺序必须是先父表后子表后面避坑章节会专门讲这个顺序问题。建完表后的标准动作是验证用 DESCRIBE student; 看字段定义再用 SHOW CREATE TABLE sc; 看完整约束。这两个命令的输出就是实验报告里「表结构」一栏的截图来源。很多同学在这一步偷懒直接截建表语句但验收老师更愿意看到的是这两个验证命令的结果因为那说明你真的执行过。2.3 视图、索引、约束实验报告里最容易被扣分的三类对象建表之后的实验题常见组合是「查询 视图 索引 约束」四件套。查询部分靠多写视图和索引没太多花样但坑不少。常见做法如下。-- 视图只暴露需要的列查询时把它当表用 CREATE VIEW v_student_course AS SELECT s.sno, s.sname, c.cname, sc.grade FROM student s JOIN sc ON s.sno sc.sno JOIN course c ON c.cno sc.cno WHERE sc.grade IS NOT NULL; -- 索引复合索引的列顺序决定查询走不走索引 CREATE INDEX idx_sc_cno_grade ON sc(cno, grade); -- 约束CHECK 约束能拦住脏数据但不是所有版本都强制生效 ALTER TABLE sc ADD CONSTRAINT chk_grade CHECK (grade BETWEEN 0 AND 100);视图这一题有三个坑。一是视图定义里用了 WHERE 过滤实验要求「修改基表数据后视图自动更新」没问题但要求「通过视图插入数据」时带过滤条件的视图在很多数据库里不能直接插入需要加 WITH CHECK OPTION这一点教材不一定写。二是索引的列顺序idx_sc_cno_grade 适合查「某门课的成绩段」如果你要查的是「某个学生的所有课程」索引顺序应该反过来否则实验报告里写「利用索引加速查询」就是空话。三是 CHECK 约束MySQL 8.0.16 之前建了也不生效实验报告如果写「用 CHECK 保证成绩在 0 到 100」在旧版本上是自欺欺人正确做法是同时用触发器兜底或者干脆在应用层校验。约束这块还有一个必考操作删除约束。常见做法是先查约束名再删别凭印象写。SHOW CREATE TABLE sc; 里能直接看到系统生成的约束名删的时候照抄。修改列类型同理ALTER TABLE ... MODIFY COLUMN 后面的定义必须写完整只写列名不写类型会直接报错。3. 从 ER 图到关系模式设计类实验的完整拆解如果说 SQL 实验是「照着要求敲代码」设计类实验就是「给出一个业务场景让你画出 ER 图再转成关系模式」。这类题没有唯一答案但判定标准很明确关系模式是否消除了冗余、插入异常、删除异常外键指向是否正确。很多同学这部分靠背一旦场景换成没见过的就全盘崩。下面给一套可复用的拆法。3.1 ER 图转关系模式映射规则与一个完整例子先记住三条基本映射规则这是所有转换题的地基。ER 图联系转换规则外键放哪1:1两个实体各成一张表任选一边放外键指向另一边任选一边建议放在查询频繁的一边1:N两实体各一张表N 端表加外键指向 1 端永远放在 N 端M:N两实体各一张表再单独建中间表中间表放两个外键一般与两端主键组成联合主键用一个场景走一遍某单位有「部门」和「员工」两个实体一个部门有多名员工一名员工只属于一个部门员工和「项目」之间是多对多一个员工参与多个项目一个项目有多名员工参与。按规则转换得到的关系模式如下。部门部门编号部门名称负责人员工员工编号姓名性别部门编号—— 部门编号是外键指向部门这就是 N 端外键项目项目编号项目名称预算参与员工编号项目编号参与时间工时—— 中间表联合主键员工编号项目编号转换题最常见的扣分点是「该建中间表的时候没建」。判断标准一句话两个实体之间如果除了「属于」之外还带着自己的属性比如参与时间、工时几乎一定是 M:N必须拆中间表不能把项目编号直接塞进员工表里。另一类扣分点是外键方向放反1:N 关系的 N 端没放外键导致部门维度的属性在员工表里重复出现后面做规范化时还得返工。3.2 规范化实验从 1NF 到 3NF/BCNF 的判定与分解规范化实验的题干通常是「分析该关系模式属于第几范式并分解到 3NF」。第一步永远是列出函数依赖而不是盯着数据看。比如有个「选课」关系模式选课学号姓名课程号课程名成绩函数依赖是学号→姓名课程号→课程名学号课程号→成绩。可以看到姓名只依赖学号课程名只依赖课程号都存在对联合主键的部分函数依赖所以它属于 1NF不是 2NF。判定的顺序是死的先找主属性再看有没有非主属性对候选键的部分依赖有就是 1NF 不是 2NF再看有没有非主属性对候选键的传递依赖有就是 2NF 不是 3NF。BCNF 进一步要求「每个决定因素都包含候选键」相对少见但实验题偶尔考。这里最容易踩的坑是跳过函数依赖直接看数据数据看着像第二范式没用范式等级只由函数依赖决定。分解到 3NF 的常见做法是「把部分依赖和传递依赖拆出去」。上面的选课关系按学号拆出「学生学号姓名」按课程号拆出「课程课程号课程名」剩下的选课学号课程号成绩。这一拆插入异常和更新异常都消失了。-- 验证拆分的正确性在未拆分的表上查冗余是课后实验常用的论证手段 -- course_choice 是故意保留的未规范化表 SELECT cno, cname, credit, COUNT(*) AS dup_rows FROM course_choice GROUP BY cno, cname, credit HAVING COUNT(*) 1;这条 SQL 在未规范化的表上跑能查出「同一门课的主修信息重复出现了多少次」是实验报告里证明「存在数据冗余、需要分解」的硬证据。但注意SQL 能证明冗余不能证明范式等级。范式等级的判定必须回到函数依赖上这是这类实验最容易混淆的地方。另外分解不是拆得越碎越好3NF 已经能解决大部分课后实验的场景。分解后如果函数依赖被弄丢了说明做法不对要检查是否做到了无损连接——最常用的验证方法是看分解后的关系能否通过自然连接还原出原关系。4. 事务与并发课后实验里最容易做成玄学的部分事务实验是很多人第一次在数据库上翻车的地方明明按照教材写了 BEGIN TRANSACTION回滚却不生效明明设置了隔离级别脏读还是出现了。原因通常不是操作错而是实验需要两个会话配合但很多人只开了一个查询窗口。4.1 隔离级别实验两个会话怎么配合隔离级别实验的标准做法是开两个连接两个会话一个执行修改另一个观察读取结果。MySQL 里用两个终端分别登录实验效果最直观。-- 会话 A把隔离级别改成 READ COMMITTED 并开启事务 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; START TRANSACTION; UPDATE account SET balance balance - 100 WHERE acc_id 1; -- 此时不要 COMMIT切到会话 B 去观察 -- 会话 B查看当前隔离级别并读取账户余额 SELECT transaction_isolation; SELECT balance FROM account WHERE acc_id 1;关键观察点如果会话 A 没提交READ COMMITTED 下会话 B 读到的是旧值如果会话 A 用的是 READ UNCOMMITTED会话 B 会直接读到未提交的新值——这就是脏读。实验报告里要求写的「隔离级别对比表」就是靠这两个会话来回切换读出来的。还有一个高频翻车点SET SESSION TRANSACTION ISOLATION LEVEL 只对当前会话生效如果写成 SET TRANSACTION ... 并且在事务执行中途修改隔离级别MySQL 不会报错但也不生效一定在 START TRANSACTION 之前设置。这类实验还要注意一个细节做完 READ COMMITTED 的对比后记得把隔离级别改回默认值否则会影响后续实验。我一般会在实验脚本最后加一行 RESET SESSION TRANSACTION ISOLATION LEVEL; 收尾避免下一个实验莫名其妙读不到数据。4.2 存储过程与触发器把批处理逻辑写进实验报告存储过程和触发器是实验题里「会的人很快不会的人靠抄」的部分。其实套路非常固定。存储过程就是「把一段 SQL 包起来给个名字允许传参」。DELIMITER // CREATE PROCEDURE sp_add_student( IN p_sno CHAR(9), IN p_sname VARCHAR(20), IN p_sdept VARCHAR(20) ) BEGIN -- 参数校验学生表有 NOT NULL 约束空值进不来就直接报错 IF p_sno IS NULL OR p_sname IS NULL THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 学号和姓名不能为空; END IF; INSERT INTO student(sno, sname, sdept) VALUES (p_sno, p_sname, p_sdept); END // DELIMITER ; -- 调用传参顺序要和 IN 参数声明一致这个顺序错了也报错 CALL sp_add_student(2024001, 张三, 计算机系);触发器则是在表上挂的「自动化动作」。课后实验最常见的需求是向学生表插入数据时自动写一条日志。CREATE TRIGGER trg_stu_insert_log AFTER INSERT ON student FOR EACH ROW INSERT INTO student_log(sno, op_type, op_time) VALUES (NEW.sno, INSERT, NOW());写触发器的三个实际坑。第一AFTER INSERT 里别再去 UPDATE 同一张 student 表会造成触发器递归很多数据库直接报错或锁死。第二触发器和外键约束一起用时执行顺序不一定符合直觉外键检查失败时触发器根本不会执行别用触发器代替外键校验。第三实验报告里触发器一定要截「触发前后」两张表的数据对比只贴触发器定义代码验收时通常不算数。存储过程同理要展示调用后的结果集或影响行数证明这段逻辑真的被执行了。5. 数据库课后实验避坑实录5 个高频翻车现场与处理办法这一章列的是课后实验里反复出现的翻车现场每个都按「现象 → 原因 → 解决」写你遇到同名问题可以直接抄处理办法。5.1 插入数据报外键约束错误顺序明明检查过现象往 sc 表插入选课记录时报外键约束失败但对应的学生和课程明明都存在。 原因多数情况下是字符集或排序规则不一致。student 表和 course 表的 sno 字段如果排序规则不同外键关系虽然能建插入时却匹配不上。另一个隐蔽原因是插入语句里 sno 带了空格或用了全角字符肉眼看不出来。 解决建库时统一用 DEFAULT CHARACTER SET utf8mb4建表时不要再单独指定其他字符集如果表已经建了把相关列的 COLLATE 改成一致再试。排查时用 SELECT QUOTE(sno) FROM student; 看字段真实内容能直接暴露隐藏的空格。外键列记得建索引某些数据库不会自动为外键列建索引数据量一大查询就慢。5.2 UPDATE 忘了 WHERE整个表被改了现象想把「计算机系所有学生的年龄加 1」执行后全校学生年龄都变了。 原因UPDATE 语句没写 WHERE 条件数据库按字面意思执行不带任何确认。这是所有数据库操作里代价最高的失误。 解决没有后悔药靠习惯。执行 UPDATE 前先写一条相同条件的 SELECT 确认影响范围这是数据库从业者的基本素养。实验数据被改坏了用重新执行建表脚本来还原所以建表脚本一定单独存文件。给个血泪经验凡是批量 UPDATE 或 DELETE我的固定动作是先 SELECT COUNT(*) 看命中行数符合预期再执行MySQL 还支持 UPDATE ... LIMIT 限制影响行数实验环境里很好用。5.3 隔离级别设置了脏读还是出现现象按教材设置了 READ UNCOMMITTED发现读到的还是旧值或者设置了 READ COMMITTED脏读照样出现。 原因两个典型。一是设置只对当前会话生效另一个会话还是默认级别两边级别不一致自然看不到教材描述的现象二是事务开始之后才执行 SET TRANSACTION当前事务不认这个设置。 解决统一用 SET SESSION TRANSACTION ISOLATION LEVEL并且在 START TRANSACTION 之前执行。验证是否生效先 SELECT transaction_isolation; 确认当前会话的值再开两个会话做脏读对比实验。做完实验把级别重置回默认避免影响后面的事务实验。5.4 中文乱码或者 LIKE 查不出中文现象插入的中文在查询结果里显示成问号或者 WHERE sname LIKE %张% 查不到数据。 原因客户端连接字符集和表的字符集不一致。建表用了 utf8mb4但连接时 character_set_client 还是 latin1数据存进去就变了。 解决建库统一 utf8mb4连接数据库后先执行 SET NAMES utf8mb4;。这个坑经常被忽略因为「在其他机器上跑就是好的」。真正的根源是每一层的字符集都要一致客户端、连接、数据库、表、列任何一层不一致都可能出问题。我的习惯是固定检查顺序先看表定义再看连接参数最后确认 SET NAMES 是否已执行。5.5 实验报告的数据和代码对不上现象报告里贴的截图是 6 行结果代码重跑一遍却是 8 行或者聚合结果数值对不上。 原因实验过程中手动 INSERT 了多次测试数据中间有失败的重试导致库里残留脏数据或者报告截图是早期版本后来改了查询条件没重新截图。 解决每份实验建立一个独立 schema实验结束前用固定顺序重跑一遍清空所有表 → 按脚本重新插入 → 执行查询 → 重新截图。这个「从零重跑」的动作能让报告里的每一步都经得起现场验证。建议把建库、插数据、查询分别存成三个 .sql 文件验收时要哪步跑哪步不要靠记忆在命令行里重敲。6. 最后一课用三个自查动作确认实验真的做对了做完实验别急着关电脑花三分钟做三个自查动作。第一个动作是「数据量核对」手算一遍预期行数再用 COUNT(*) 对照。比如查询「每门课的选课人数」先数清楚 course 表有几门课再确认 GROUP BY 结果行数一致不一致一定漏了数据或 WHERE 条件写错。第二个动作是「边界验证」特意插入一条违反约束的数据看数据库是不是真的拦截。成绩填 101 分、学号填空、部门编号填一个不存在的值每拦下一批就说明约束在起作用这也是实验报告里最有含金量的截图。第三个动作是「从零重跑」把建库建表、插入数据、全部查询依次重新执行一遍确认每一步都无报错。这一步能筛掉 80% 的验收翻车。我自己早年做实验时最惨的一次教训就是第三个动作没做验收现场重跑时发现插入脚本里少了一条数据整份报告的数据对不上只能当场改口。从那以后每份实验都固定留三个文件01_schema.sql、02_data.sql、03_queries.sql改代码就重跑全套。这个习惯后来在工作里也一直用着。数据库课后实验说到底不是考背诵是考你能不能让数据库按你的设计稳定运行。按这套流程把环境、设计、事务、验证都走一遍剩下的就是多敲几遍让手记住语法。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。