数据库实验五通关指南:存储过程、触发器、游标与事务 简介这份资源是西北工业大学软件学院数据库课程的实验五配套材料面向正在学习数据库设计、需要完成E-Commerce项目ER建模的学生。内容围绕电商项目描述文档展开要求读者据此构建完整的ER模式属于从需求分析到概念模型设计的实践训练。压缩包共20个文件约282KB以14个gif操作截图、2个txt说明、2个doc文档及1个cdm概念模型文件、1个htm页面为主截图直观呈现注册、购物车、结算、订单确认等业务流程cdm与ER图文件则给出概念数据模型参考。已有911人学习下载说明该实验在课程学习中具有较高参考价值。读者可借助项目描述文档理解建模需求对照ER图与cdm文件检查实体、联系与属性的设计是否完整并通过流程截图把握电商场景中的关键业务环节从而独立完成实验并加深对概念模型与需求映射关系的理解。1. 数据库实验五到底在练什么一张地图先摆出来很多人复习数据库实验习惯从 SQL 语法开始背背到游标、触发器就卡住最后发现实验报告写不出来不是因为不会写语句而是因为脑子里没有一张「这个实验在练什么能力」的地图。数据库实验五通常落在存储过程、触发器、游标、事务与权限这一组偏「服务端逻辑」的内容上它和前面几次实验最大的区别是前面你在写「查什么」这次你在写「什么时候自动做什么」。这个转向不完成后面所有语法都是散的。这份来自某高校软件学院数据库课程的实验五资料价值不在于给你一份可抄的答案而在于它把「过程化 SQL 如何嵌进真实业务约束」这条线完整走了一遍。适合已经学过基础 DDL/DML、正在准备实验验收或期末复习的人用来把零散语法重新挂到一张体系图上。2. 存储过程与函数把业务规则收进数据库2.1 存储过程的结构与参数模式存储过程是实验五最先要啃下的模块因为它同时考三件事语法结构、参数传递、异常处理。梳理下来会发现大部分翻车都发生在参数模式上而不是逻辑本身。以常见的 T-SQL 风格为例一个带输入输出参数的存储过程骨架是这样的-- 按班级统计选课人数并通过输出参数返回结果 CREATE PROCEDURE Proc_CountByClass ClassName NVARCHAR(50), -- 输入参数班级名 StuCount INT OUTPUT -- 输出参数人数结果 AS BEGIN SET NOCOUNT ON; -- 关闭行数提示避免干扰调用方 SELECT StuCount COUNT(*) FROM Student WHERE ClassName ClassName; IF StuCount IS NULL -- 无匹配时归零防止调用方拿到 NULL SET StuCount 0; END;这段代码里三个点值得单独记OUTPUT关键字必须和调用方的变量声明配套SET NOCOUNT ON不是可有可无的装饰它影响的是客户端拿到的结果集行为IF ... IS NULL那一段是血泪经验——聚合函数在无匹配行时返回的是 0 还是 NULL取决于写法不处理就会在验收时被追问。调用侧要写成DECLARE n INT; EXEC Proc_CountByClass ClassName N某班, StuCount n OUTPUT;参数顺序和命名两种写法都要会因为不同教材示例混着用。参数模式对比可以整理成一张表复习时直接对照参数模式关键字方向典型用途易错点输入默认 / IN调用方 → 过程查询条件、过滤值字符串长度截断输出OUTPUT / OUT过程 → 调用方计数、状态码调用方漏写 OUTPUT输入输出OUTPUT 兼作入参双向累加、游标位置初值未赋导致 NULL 参与运算2.2 函数与存储过程的边界实验五常要求「用函数实现某计算、用存储过程实现某流程」很多人把两者混着写结果函数里出现INSERT直接编译不过。判断标准其实很干脆函数强调「给参数、还一个值」不允许改数据、不允许事务控制存储过程强调「做一串动作」可以有副作用。复习时把这条边界记成一句话——函数是表达式过程是动作。函数里如果非要读表注意不同数据库对「函数内能否查询表」的限制不一样写之前先确认教材用的是哪种方言别把 MySQL 的写法搬到 SQL Server 上。3. 触发器自动化的代价与约束3.1 触发器的三类时机与两张虚拟表触发器是实验五的重点也是难点因为它把「隐式执行」这件事摆到了台面上。按时机分常见的是AFTER语句成功后触发和INSTEAD OF替代原操作执行按粒度分有行级和语句级。真正让初学者绕晕的是虚拟表INSERTED和DELETED。记住一句口诀——插入看 INSERTED删除看 DELETED更新两张都要看。更新操作在逻辑上等于「先删旧行、再插新行」所以UPDATE触发器里DELETED存旧值、INSERTED存新值这个对应关系不建立写出来的审计日志永远是错的。-- 记录成绩修改前后的对比写入审计表 CREATE TRIGGER Trg_ScoreAudit ON Score AFTER UPDATE AS BEGIN INSERT INTO ScoreAudit (StuId, CourseId, OldScore, NewScore, ChangeTime) SELECT d.StuId, d.CourseId, d.Score, i.Score, GETDATE() FROM DELETED d JOIN INSERTED i ON d.StuId i.StuId AND d.CourseId i.CourseId; END;这里用JOIN而不是逐行处理是因为触发器按语句触发时INSERTED和DELETED可能是多行集合逐行假设会漏数据。这是实验验收最常被挑的毛病之一。3.2 递归触发与禁用场景触发器里再改同一张表可能引发递归触发不同数据库默认行为不同有的直接报错有的静默递归到层数上限。复习时要知道两件事一是怎么临时禁用触发器如ALTER TABLE ... DISABLE TRIGGER二是为什么生产环境慎用触发器做复杂业务——它把逻辑藏进了「看不见的地方」排查问题时像黑匣子。实验里用它做审计、做级联约束是合理的用它做跨表大范围数据同步就要掂量。4. 游标与事务逐行处理的正确姿势4.1 游标的生命周期四步游标考的是「集合思维切回行思维」的能力标准生命周期是声明、打开、取数循环、关闭并释放。四步缺一不可漏掉DEALLOCATE在部分数据库里会持续占用资源。典型写法DECLARE sid INT, score INT; DECLARE Cur_Score CURSOR FOR SELECT StuId, Score FROM Score WHERE Score 60; -- 只处理不及格记录 OPEN Cur_Score; FETCH NEXT FROM Cur_Score INTO sid, score; WHILE FETCH_STATUS 0 -- 0 表示取数成功 BEGIN -- 对每条不及格记录做处理例如写入补考名单 INSERT INTO MakeupExam (StuId, Reason) VALUES (sid, N成绩不及格); FETCH NEXT FROM Cur_Score INTO sid, score; END; CLOSE Cur_Score; DEALLOCATE Cur_Score;FETCH_STATUS的判断位置是易错核心必须先取一次再判断循环体末尾再取一次写成「先判断后取」就会少处理一行或多跑一轮。另外游标分只读、可滚动、动态等多种类型实验里通常用默认的只进只读即可不必炫技。4.2 事务的 ACID 与隔离级别取舍事务模块要能把 ACID 四个字母落到具体语句上原子性靠BEGIN TRAN / COMMIT / ROLLBACK一致性靠约束和触发器共同保证隔离性靠隔离级别持久性靠日志。实验五常要求「在存储过程里包一层事务出错回滚」写法上要配合TRY...CATCHBEGIN TRY BEGIN TRANSACTION; UPDATE Account SET Balance Balance - 100 WHERE Id 1; UPDATE Account SET Balance Balance 100 WHERE Id 2; COMMIT TRANSACTION; END TRY BEGIN CATCH IF TRANCOUNT 0 ROLLBACK TRANSACTION; -- 确保异常时事务被撤销 THROW; -- 把错误继续抛给调用方 END CATCH;TRANCOUNT 0这个判断是后悔药防止在事务已回滚的情况下重复回滚报错。隔离级别则要能说出「读未提交、读已提交、可重复读、可串行化」各自解决哪种异常脏读、不可重复读、幻读复习时用一张对照表记比背定义快得多。5. 易错点清单验收前必须自查的五处5.1 五个高频翻车点易漏点一参数与列名同名导致歧义。为什么漏写的时候觉得「名字一样更直观」编译能过但结果错。怎么补参数统一加前缀如StuId对列StuId或在语句里用表别名限定列。易漏点二触发器里假设单行。为什么漏测试数据只插了一行恰好没暴露。怎么补所有触发器逻辑按集合写用INSERTED/DELETED直接参与INSERT ... SELECT不写逐行变量。易漏点三游标忘记DEALLOCATE。为什么漏单次执行看不出问题反复调用才积累。怎么补把「关闭释放」当成固定收尾模板写完立刻补上。易漏点四事务里混入 DDL。为什么漏以为CREATE TABLE也能回滚。怎么补记住多数数据库的 DDL 会隐式提交事务只包 DML建表建索引放到事务外。易漏点五权限授予粒度失控。为什么漏实验只验证「能执行」不验证「该不该」。怎么补用GRANT EXECUTE ON 对象 TO 用户精确到对象避免GRANT ALL复习时把「最小权限」当默认习惯。5.2 自查顺序建议验收前按「语法能编译 → 单行数据正确 → 多行数据正确 → 异常路径回滚 → 权限边界」五步走一遍比从头读代码有效。多行测试是分水岭很多逻辑错误只在批量数据下现形。6. 复习落地把实验五压缩成一张自查表复习到这一步重点不是再刷一遍语法而是把每个模块压缩成「能自问自答」的条目。我的做法是给实验五建一张四列自查表模块、核心语句、必测场景、常见错误。存储过程那行写「参数模式 异常处理」必测「无匹配行、输出参数回传」触发器那行写「AFTER/INSTEAD OF 虚拟表」必测「批量更新、递归风险」游标那行写「四步生命周期」必测「空结果集、多行遍历」事务那行写「TRY/CATCH 隔离级别」必测「中途异常回滚」。表格填不满的地方就是你要回去补的洞。模块一句话本质必测场景高频错误存储过程带参数的动作封装无匹配、输出参数漏 OUTPUT、NULL 未处理函数有返回值的表达式边界输入函数内写 DML触发器隐式执行的约束批量更新假设单行游标集合转逐行空集、多行取数顺序错、未释放事务一组操作的原子边界异常回滚DDL 混入、重复回滚查漏补缺的具体技巧是「反向出题」拿实验要求里的每个功能点自己造一条会失败的输入看系统是否按预期拒绝或回滚。能造出失败用例说明你真的理解了约束边界造不出来多半是只记住了成功路径。这份实验资料里每个模块都配了可运行的示例正好拿来当反向测试的底稿把参数改一改、数据加几行比干看代码有用得多。从那以后我每次复习数据库实验都强制自己先画模块地图再动键盘写完一个对象立刻补一条失败用例验收前按五步自查走一遍再没在「多行数据」和「异常回滚」上翻过车。希望这份梳理帮到你。本文还有配套的精品资源点击获取