简介这份资源是西北工业大学《数据库原理》课程的第五次实验报告文档面向正在学习数据库课程的高校学生及需要巩固SQL Server实操的开发者。内容围绕数据库对象的核心操作展开涵盖使用sp_rename重命名视图、创建带参数的jsearch存储过程与加密的jmsearch存储过程、借助sp_helptext查看过程文本以及针对SC表和S表设计insert_s、dele_s1、dele_s2、update_s等多个触发器并涉及禁用与删除触发器的完整流程。资源包内仅含1个doc文件大小约231KB属于纯文档型实验资料便于直接查阅与打印。目前已有716人学习浏览说明其在同类实验报告中具有一定参考价值。读者可借此获得完整的实验步骤、可复用的SQL代码片段与触发器验证思路适合作为课程作业参考或期末复习的实操范本。1. 数据库实验报告5到底在做什么从一份文档名反推整套实验链路看到“数据库实验报告5.doc”这个标题很多人第一反应是找模板、找答案、找能直接交差的文档。但真正做过数据库课程实验的人都知道实验报告5通常对应的是整个实验体系里最综合的那一环——它不再是单表增删改查而是把建库、建表、约束、索引、视图、存储过程、触发器、事务控制串成一条完整链路。换句话说这份文档背后真正要交付的不是几段SQL而是一套能跑通、能解释、能复现的数据库方案。我见过太多同学前面四个实验靠复制粘贴混过去到实验5直接翻车表建好了但外键顺序不对数据插不进去视图查出来和预期差三行排查半天发现是NULL参与比较存储过程调试时参数传错类型报错信息又看不懂。这些坑不是玄学是数据库实验里反复出现的结构性问题。这篇文章面向三类人正在做数据库综合实验、需要把实验5从建表到事务完整落地的人已经写完但跑不通、想系统排查的人以及想把实验报告写成“可复现技术文档”而不是“截图堆砌”的人。下面按建库建表、查询与视图、存储过程与触发器、事务与并发、避坑排查、进阶验证的顺序展开每一步都给可抄的SQL和参数说明。2. 建库建表与约束设计先把地基打对再谈查询2.1 从实验5的典型需求反推表结构数据库实验5通常给一个业务场景比如“学生选课系统”“图书借阅系统”“简易订单系统”。不管场景叫什么核心都是三到五张表加若干关联。我一般先做三件事确定实体、确定关系基数、确定哪些字段必须唯一。以选课场景为例实体是学生、课程、选课记录。学生和课程是多对多所以选课记录是中间表。中间表的主键有两种常见做法一是用自增ID做代理主键二是用(学生ID, 课程ID)做联合主键。实验报告里如果没明确要求我倾向联合主键因为它天然防止重复选课少一个唯一索引。-- 建库字符集用utf8mb4避免中文和特殊符号乱码 CREATE DATABASE IF NOT EXISTS lab5_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE lab5_db; -- 学生表学号做主键姓名非空年龄加检查约束 CREATE TABLE student ( stu_id VARCHAR(12) NOT NULL, stu_name VARCHAR(30) NOT NULL, age TINYINT UNSIGNED, gender CHAR(1) DEFAULT M, PRIMARY KEY (stu_id), CONSTRAINT chk_age CHECK (age IS NULL OR (age 15 AND age 60)) ) ENGINEInnoDB; -- 课程表课程号主键学分限制在合理范围 CREATE TABLE course ( course_id VARCHAR(10) NOT NULL, course_name VARCHAR(50) NOT NULL, credit DECIMAL(3,1) NOT NULL DEFAULT 2.0, PRIMARY KEY (course_id), CONSTRAINT chk_credit CHECK (credit 0 AND credit 10) ) ENGINEInnoDB; -- 选课表联合主键防重复外键分别指向学生和课程 CREATE TABLE sc ( stu_id VARCHAR(12) NOT NULL, course_id VARCHAR(10) NOT NULL, score DECIMAL(5,2), select_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (stu_id, course_id), CONSTRAINT fk_sc_stu FOREIGN KEY (stu_id) REFERENCES student(stu_id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT fk_sc_course FOREIGN KEY (course_id) REFERENCES course(course_id) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINEInnoDB;这段代码的关键不在语法而在三个决策。第一存储引擎必须显式写InnoDB因为实验5大概率要用事务MyISAM不支持。第二外键的ON DELETE策略要区分选课记录跟着学生走学生删了选课记录没意义所以CASCADE但课程被选后不能随便删所以RESTRICT。第三CHECK约束在MySQL 8.0.16之后才真正生效如果实验室环境是5.7写了也不报错但不执行报告里要说明这一点否则老师一问就露馅。2.2 插入测试数据的顺序与批量写法建表之后插数据顺序必须是先父表后子表否则外键直接拒绝。我习惯用一条多值INSERT而不是多条单值INSERT减少事务开销也方便回滚测试。-- 先插学生和课程再插选课记录 INSERT INTO student (stu_id, stu_name, age, gender) VALUES (2024001, 张一, 20, M), (2024002, 李二, 21, F), (2024003, 王三, 19, M), (2024004, 赵四, 22, F); INSERT INTO course (course_id, course_name, credit) VALUES (C001, 数据库原理, 4.0), (C002, 操作系统, 3.5), (C003, 计算机网络, 3.0); INSERT INTO sc (stu_id, course_id, score) VALUES (2024001, C001, 88.5), (2024001, C002, 76.0), (2024002, C001, 92.0), (2024003, C003, NULL), (2024004, C002, 81.5);注意最后一条score是NULL这是故意的。实验5的查询题经常考NULL处理比如“查所有没出成绩的选课记录”用WHERE score NULL永远查不到必须用IS NULL。这个点在报告里写一句比贴十张截图都有说服力。2.3 索引不是越多越好实验5里该建哪几个实验报告常要求“为常用查询建索引”。我的原则是外键列自动有索引InnoDB会建联合主键的第二列如果单独查要补索引经常出现在WHERE和ORDER BY里的列考虑建。-- 按课程查选课情况联合主键里course_id是第二列单独查不会走主键索引 CREATE INDEX idx_sc_course ON sc(course_id); -- 按成绩范围查score选择性不高时索引可能不划算但实验里可以建了对比 CREATE INDEX idx_sc_score ON sc(score); -- 查看执行计划确认索引是否被使用 EXPLAIN SELECT * FROM sc WHERE course_id C001;EXPLAIN的输出重点看type和key两列。type是ALL表示全表扫描ref或range表示走了索引。如果建了索引但type还是ALL可能是数据量太小优化器觉得没必要实验报告里可以插几百行数据再测效果更明显。3. 查询、视图与存储过程把业务逻辑写进数据库3.1 多表连接查询的三种写法和适用场景实验5的查询题一般要求“查询每个学生的选课门数和平均分”“查询没人选的课程”这类。前者用INNER JOIN加GROUP BY后者用LEFT JOIN加IS NULL判断。-- 每个学生的选课门数和平均分没选课的学生也要显示用LEFT JOIN SELECT s.stu_id, s.stu_name, COUNT(sc.course_id) AS course_count, ROUND(AVG(sc.score), 2) AS avg_score FROM student s LEFT JOIN sc ON s.stu_id sc.stu_id GROUP BY s.stu_id, s.stu_name ORDER BY avg_score DESC; -- 没人选的课程从课程表左连接选课表选课表主键为NULL的就是没人选 SELECT c.course_id, c.course_name FROM course c LEFT JOIN sc ON c.course_id sc.course_id WHERE sc.stu_id IS NULL;这里有个细节COUNT(sc.course_id)和COUNT()在LEFT JOIN下结果不同。COUNT()会统计包含NULL的行没选课的学生会算1COUNT(sc.course_id)只统计非NULL没选课的学生算0。实验报告里如果写“选课门数”必须用COUNT(sc.course_id)否则数据是错的。3.2 视图的创建与“视图能不能更新”的边界视图在实验5里通常要求“创建一个视图展示学生选课详情”。视图的好处是封装复杂查询但很多人不知道视图的更新限制。-- 创建视图展示学生姓名、课程名、成绩 CREATE OR REPLACE VIEW v_stu_course AS SELECT s.stu_id, s.stu_name, c.course_name, sc.score FROM student s JOIN sc ON s.stu_id sc.stu_id JOIN course c ON sc.course_id c.course_id; -- 查询视图 SELECT * FROM v_stu_course WHERE score 80;这个视图包含三表连接属于不可更新视图。如果实验要求“通过视图修改成绩”必须改成单表视图或者用INSTEAD OF触发器MySQL不支持INSTEAD OF只能用存储过程替代。常见做法是视图只读更新走基表。报告里写清楚“本视图用于查询更新操作通过存储过程实现”逻辑就闭环了。3.3 存储过程参数、流程控制与调试方法存储过程是实验5区分“会写SQL”和“会做数据库开发”的分水岭。我一般写一个带输入输出参数的存储过程比如“根据学号查平均分并返回等级”。DELIMITER $$ CREATE PROCEDURE sp_get_stu_level( IN p_stu_id VARCHAR(12), OUT p_avg_score DECIMAL(5,2), OUT p_level VARCHAR(10) ) BEGIN -- 先算平均分注意NULL处理 SELECT ROUND(AVG(score), 2) INTO p_avg_score FROM sc WHERE stu_id p_stu_id; -- 如果没选课平均分为NULL直接返回未知 IF p_avg_score IS NULL THEN SET p_level 无成绩; ELSEIF p_avg_score 90 THEN SET p_level 优秀; ELSEIF p_avg_score 80 THEN SET p_level 良好; ELSEIF p_avg_score 60 THEN SET p_level 及格; ELSE SET p_level 不及格; END IF; END$$ DELIMITER ; -- 调用并查看输出 CALL sp_get_stu_level(2024001, avg, lvl); SELECT avg AS avg_score, lvl AS level;调试存储过程最常见的翻车是DELIMITER没改回来导致后续SQL全部报错。我的习惯是写完存储过程立刻执行DELIMITER ;再继续写其他语句。另外OUT参数在调用时必须用用户变量开头不能直接用常量。4. 触发器与事务实验5最容易翻车的两个点4.1 触发器实现业务规则选课人数上限与日志记录触发器常考“选课人数超过上限时阻止插入”或“成绩修改后记录日志”。我写一个前者因为逻辑更完整。-- 先建一个日志表记录被拒绝的选课尝试 CREATE TABLE sc_log ( log_id INT AUTO_INCREMENT PRIMARY KEY, stu_id VARCHAR(12), course_id VARCHAR(10), log_time DATETIME DEFAULT CURRENT_TIMESTAMP, reason VARCHAR(100) ) ENGINEInnoDB; DELIMITER $$ CREATE TRIGGER trg_sc_limit BEFORE INSERT ON sc FOR EACH ROW BEGIN DECLARE v_count INT; -- 查该课程已选人数 SELECT COUNT(*) INTO v_count FROM sc WHERE course_id NEW.course_id; -- 假设每门课上限5人 IF v_count 5 THEN -- 记录日志后抛错阻止插入 INSERT INTO sc_log (stu_id, course_id, reason) VALUES (NEW.stu_id, NEW.course_id, 课程人数已满); SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 选课人数已达上限插入被拒绝; END IF; END$$ DELIMITER ;触发器的坑在于SIGNAL抛错后触发器里之前的INSERT写日志也会被回滚除非日志表用MyISAM。但MyISAM不支持事务实验里如果要求“日志必须保留”就得换思路——比如用独立连接写日志或者接受日志也回滚。我一般会在报告里说明这个权衡老师反而觉得你理解深。4.2 事务隔离级别与并发问题复现实验5通常要求“演示事务的ACID”或“复现脏读/不可重复读”。MySQL默认隔离级别是REPEATABLE READ要复现脏读需要改成READ UNCOMMITTED。-- 会话A查看当前隔离级别 SELECT transaction_isolation; -- 会话A设置读未提交 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 会话A开启事务并修改数据但不提交 START TRANSACTION; UPDATE sc SET score 100 WHERE stu_id 2024001 AND course_id C001; -- 会话B在另一个连接里查能读到未提交的100这就是脏读 SELECT score FROM sc WHERE stu_id 2024001 AND course_id C001; -- 会话A回滚 ROLLBACK; -- 会话B再查数据变回原值 SELECT score FROM sc WHERE stu_id 2024001 AND course_id C001;这个实验需要两个客户端连接同时操作报告里要写清楚“会话A”和“会话B”分别执行什么。很多人只在一个窗口里跑结果永远复现不了。另外MySQL 8.0的隔离级别变量名是transaction_isolation5.7是tx_isolation写报告时注意版本差异。5. 数据库实验5的避坑与排查5条血泪经验5.1 外键报错1452插入顺序和数据类型都要查现象插入选课记录时报Cannot add or update a child row: a foreign key constraint fails。原因通常有两个父表里没有对应的主键值或者外键列和主键列的数据类型/字符集不一致。解决先SELECT父表确认值存在再用SHOW CREATE TABLE对比两表的列定义字符集不同也会导致外键建不上。5.2 视图查不到数据NULL比较和连接条件写错现象视图创建成功但查出来行数比预期少。原因连接条件里用了比较可能为NULL的列或者WHERE里写了score NULL。解决把改成IS NULL或NULL安全等于连接条件逐列核对。5.3 存储过程创建失败DELIMITER和权限问题现象CREATE PROCEDURE报语法错误或者提示Access denied。原因没改DELIMITER导致分号提前结束语句或者当前用户没有CREATE ROUTINE权限。解决先DELIMITER $$再写过程结束后改回DELIMITER ;权限问题找管理员授权或换有权限的账号。5.4 触发器导致死锁同表读写要小心现象插入数据时卡住最后报Deadlock found。原因触发器里又去查同一张表InnoDB在并发下容易死锁。解决触发器里尽量只做简单判断避免复杂查询或者把逻辑挪到应用层用事务加锁控制。5.5 事务不回滚DDL语句的隐式提交现象START TRANSACTION后执行了CREATE TABLE或ALTER TABLE再ROLLBACK发现之前的DML也没回滚。原因MySQL里DDL会隐式提交当前事务。解决事务里只放DML建表改表放在事务外报告里要说明这个行为否则实验结论就是错的。6. 进阶验证用执行计划和慢查询日志证明你的方案有效实验报告写到“能跑通”只是及格写到“能证明为什么这样跑更快”才是拉开差距的地方。我一般会加两个验证EXPLAIN看索引命中慢查询日志看实际耗时。-- 开启慢查询日志阈值设为0.1秒 SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 0.1; -- 执行一个可能慢的查询按成绩范围查并排序 SELECT * FROM sc WHERE score BETWEEN 60 AND 90 ORDER BY score DESC; -- 查看慢查询日志路径 SHOW VARIABLES LIKE slow_query_log_file; -- 用EXPLAIN分析同一查询 EXPLAIN SELECT * FROM sc WHERE score BETWEEN 60 AND 90 ORDER BY score DESC;如果EXPLAIN的type是ALL且Extra里出现Using filesort说明没走索引还额外排序。可以建复合索引(score)再测对比key列的变化。报告里放一张前后对比表比任何文字都有力。验证项优化前优化后查询类型全表扫描范围索引扫描EXPLAIN typeALLrangeExtraUsing filesortUsing index condition预估行数全表匹配行数最后说一个我自己的习惯每次写完实验5的SQL脚本一定从头到尾在一个干净数据库里重跑一遍不跳步、不注释、不依赖之前的环境。因为实验报告交上去老师很可能就是按顺序执行你的脚本中间任何一步依赖了“我之前手动插的数据”都会导致复现失败。这个习惯帮我省了至少三次返工。希望帮到你。本文还有配套的精品资源点击获取