ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

西南交大数据库原理实验全集:从建库到事务的完整SQL实战指南

西南交大数据库原理实验全集:从建库到事务的完整SQL实战指南 简介这份资源是西南交通大学《数据库原理实验》课程的实验与课程设计全集面向软件工程、人工智能等专业正在学习数据库课程的学生以及需要完成实验报告和课程设计任务的学习者。压缩包共收录10个文件以9个SQL脚本和1份docx实验报告为主整体约1.42MBSQL文件覆盖LAB-1至LAB-6的分次实验查询代码以及课程设计相关脚本docx则对应实验报告的撰写参考。内容围绕ER模型、关系代数、SQL语法等核心概念展开涉及SELECT查询、INSERT、UPDATE、DELETE等基础操作也包含JOIN连接、子查询等进阶用法课程设计部分还延伸到数据库设计、索引建立、性能优化与权限控制等实战主题。目前已有482人学习下载适合对照实验进度逐步练习SQL编写、梳理实验报告结构并借助课程设计案例理解从建库建表到查询优化的完整流程为后续软件开发与数据分析工作打下基础。1. 西南交大《数据库原理实验》这套东西到底能帮你省下多少事如果你正在上数据库原理这门课或者带这门课的实验大概率会遇到一个很具体的困境课本上讲范式、讲事务、讲索引每一条都懂但一打开 SQL Server 或者 MySQL 的查询窗口面对一个空数据库不知道从哪张表开始建。西南交大这套《数据库原理实验》全集本质上就是把这个「从零到跑通」的过程完整记录下来了——实验报告加 SQL 代码覆盖建库建表、数据操作、视图、存储过程、触发器、事务与并发控制这些核心环节。它适合三类人一是正在跟实验课、需要交报告但卡在某个步骤上的学生二是想拿一套完整案例来练手的自学者毕竟光看语法和真跑一遍是两回事三是需要课程设计选题参考的人这套材料里的表结构设计和业务场景可以直接拿来改造。需要说清楚的是这套东西是「参考」性质不是标准答案不同学校的实验要求、DBMS 版本、命名规范都有差异照抄大概率会在细节上翻车。但它的价值在于给你一条可复现的路径让你知道每一步该做什么、为什么这么做。2. 从建库到事务这套实验全集的技术骨架拆解2.1 实验内容到底覆盖了哪些数据库核心能力一套完整的数据库原理实验通常不是随便建几张表就完事。从这套材料的标题和常见课程设计结构来看它至少覆盖以下几个层次的能力训练。第一层是数据定义也就是 DDL。建数据库、建表、定义主键外键、设置约束条件。这一层看起来简单但实际动手时最容易出问题——字段类型选错、外键引用顺序不对、字符集不匹配都会导致后面所有操作报错。实验报告里通常会记录每次建表的完整语句和当时的报错信息这部分对新手特别有价值因为你能看到别人踩过的坑长什么样。第二层是数据操作DML。插入、更新、删除、查询。查询是重头戏单表查询、多表连接、子查询、聚合函数、分组过滤这些在实验里会反复练。很多人在这一步开始感受到 SQL 的威力也开始感受到它的复杂度——一个多表连接写错连接条件结果集直接爆炸。第三层是数据库对象视图、索引、存储过程、触发器。视图简化查询索引加速检索存储过程封装业务逻辑触发器实现自动响应。这一层是从「会写 SQL」到「会用数据库」的分水岭。实验里通常会要求你创建一个视图来简化某个复杂查询或者写一个触发器来维护数据一致性。第四层是事务与并发控制。这是数据库原理课的理论核心在实践中的体现。事务的 ACID 特性、隔离级别、锁机制这些概念在课本上是一段话在实验里就是几条 BEGIN TRANSACTION、COMMIT、ROLLBACK 语句以及两个会话同时操作同一行数据时的等待和冲突。这部分实验往往最能让人理解「为什么需要事务」。第五层是数据库设计与范式。从需求分析到 E-R 图再到关系模式分解最后落到具体的表结构。课程设计通常要求你走完这个完整流程而实验报告里的设计部分就是你的参考模板。2.2 用 SQL Server 跑通第一个实验的最小步骤假设你拿到了一套实验代码第一件事不是直接全选执行而是先搞清楚执行顺序。下面是一个典型的最小可复现流程以 SQL Server 为例。-- 第1步创建数据库如果不存在 -- 注意数据库文件路径要根据自己机器的实际情况修改 IF NOT EXISTS (SELECT name FROM sys.databases WHERE name NLabDB) BEGIN CREATE DATABASE LabDB ON PRIMARY ( NAME NLabDB_Data, FILENAME ND:\SQLData\LabDB_Data.mdf, SIZE 10MB, MAXSIZE 100MB, FILEGROWTH 5MB ) LOG ON ( NAME NLabDB_Log, FILENAME ND:\SQLData\LabDB_Log.ldf, SIZE 5MB, MAXSIZE 50MB, FILEGROWTH 2MB ); END GO -- 第2步切换到新建的数据库 USE LabDB; GO -- 第3步创建基础表以学生-课程-选课为例 -- 先建被引用的表再建引用表避免外键报错 CREATE TABLE Student ( Sno CHAR(9) NOT NULL PRIMARY KEY, -- 学号定长9位 Sname NVARCHAR(20) NOT NULL, -- 姓名支持中文 Ssex NCHAR(1) CHECK (Ssex IN (N男, N女)), Sage SMALLINT CHECK (Sage BETWEEN 15 AND 60), Sdept NVARCHAR(30) -- 所在系 ); GO CREATE TABLE Course ( Cno CHAR(4) NOT NULL PRIMARY KEY, -- 课程号 Cname NVARCHAR(40) NOT NULL, Ccredit SMALLINT CHECK (Ccredit 0), -- 学分必须为正 Cpno CHAR(4) NULL -- 先修课自引用外键 ); GO CREATE TABLE SC ( Sno CHAR(9) NOT NULL, Cno CHAR(4) NOT NULL, Grade DECIMAL(5,1) CHECK (Grade BETWEEN 0 AND 100), PRIMARY KEY (Sno, Cno), FOREIGN KEY (Sno) REFERENCES Student(Sno), FOREIGN KEY (Cno) REFERENCES Course(Cno) ); GO这段代码的逻辑很直白先确保数据库存在再切进去然后按依赖顺序建表。Student 和 Course 是基础表SC 是关联表所以 SC 最后建。参数上要注意几个点CHAR 和 NVARCHAR 的区别在于是否支持 Unicode中文姓名必须用 NVARCHAR 或 NCHARCHECK 约束是实验报告里经常被忽略但老师会检查的地方外键引用要求被引用列必须有 PRIMARY KEY 或 UNIQUE 约束。执行完建表之后插入数据也有顺序要求。先插 Student 和 Course再插 SC否则外键约束会直接拒绝插入。-- 插入数据先父表后子表 INSERT INTO Student VALUES (202100001, N张三, N男, 20, N计算机系); INSERT INTO Student VALUES (202100002, N李四, N女, 21, N软件工程系); INSERT INTO Course VALUES (C001, N数据库原理, 4, NULL); INSERT INTO Course VALUES (C002, N数据结构, 4, NULL); INSERT INTO SC VALUES (202100001, C001, 88.5); INSERT INTO SC VALUES (202100001, C002, 92.0); INSERT INTO SC VALUES (202100002, C001, 76.0);如果这一步报错最常见的原因是外键引用的值在父表里不存在或者 CHECK 约束不满足。比如往 SC 里插一个 Student 表里没有的学号就会直接报外键冲突。实验报告里通常会把这类报错截图记录下来你在复现的时候可以对照着看。2.3 查询实验里最容易暴露问题的三个写法查询是实验里占比最大的部分也是最能拉开差距的地方。下面三个写法几乎每套实验都会出现但新手经常写错。第一个是多表连接。很多人的第一反应是写笛卡尔积再过滤比如SELECT * FROM Student, SC WHERE Student.Sno SC.Sno。这种写法在结果上没问题但可读性差而且一旦漏写连接条件结果集就是两张表的行数相乘。更规范的写法是显式 JOIN-- 查询每个学生的选课情况和成绩 -- 用 LEFT JOIN 保证没选课的学生也能显示出来 SELECT s.Sno, s.Sname, c.Cname, sc.Grade FROM Student s LEFT JOIN SC sc ON s.Sno sc.Sno LEFT JOIN Course c ON sc.Cno c.Cno ORDER BY s.Sno, c.Cno;这里用 LEFT JOIN 而不是 INNER JOIN是因为实验里经常要求「查询所有学生包括没选课的」。如果用 INNER JOIN没选课的学生直接消失了结果就不对。这个细节在实验报告里通常会有对比说明。第二个是子查询和 EXISTS 的选择。比如「查询选修了 C001 课程的学生姓名」用 IN 和用 EXISTS 都能做但语义上有细微差别。IN 适合子查询结果集小的情况EXISTS 适合外层表大、子查询能快速命中的情况。实验里一般两种都要求写一遍让你感受执行计划的差异。第三个是聚合查询里的 GROUP BY 和 HAVING。WHERE 过滤的是行HAVING 过滤的是组这个区别在实验里会反复考。比如「查询平均成绩大于 80 分的课程」平均成绩是聚合结果必须用 HAVING-- 查询平均成绩大于80的课程号及平均分 SELECT Cno, AVG(Grade) AS AvgGrade FROM SC GROUP BY Cno HAVING AVG(Grade) 80;如果写成 WHERE AVG(Grade) 80SQL Server 会直接报错因为 WHERE 里不能用聚合函数。这个错误在实验报告里出现的频率极高几乎每个人都会踩一次。3. 事务、锁与并发实验里最像「玄学」的部分怎么破3.1 事务隔离级别在实验里到底怎么观察事务是数据库原理的理论重点但实验里怎么体现通常的做法是开两个查询窗口模拟两个用户同时操作同一张表然后观察阻塞和隔离效果。先看一个最简单的场景脏读。把隔离级别设为 READ UNCOMMITTED一个事务修改了数据但还没提交另一个事务就能读到这个未提交的值。-- 窗口A开启事务修改数据但不提交 USE LabDB; BEGIN TRANSACTION; UPDATE SC SET Grade 100 WHERE Sno 202100001 AND Cno C001; -- 注意这里故意不 COMMIT保持事务打开 -- 窗口B设置脏读隔离级别读取窗口A未提交的数据 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT Grade FROM SC WHERE Sno 202100001 AND Cno C001; -- 此时窗口B读到的 Grade 是 100但窗口A还没提交窗口B读到的 100 就是脏数据因为窗口A随时可能回滚。如果把隔离级别改成 READ COMMITTEDSQL Server 的默认级别窗口B的查询会被阻塞直到窗口A提交或回滚。这个阻塞现象在实验里非常直观——查询窗口一直转圈直到你在窗口A执行 COMMIT 或 ROLLBACK。实验报告里通常会要求你记录不同隔离级别下的现象对比。下面这张表是我自己整理过的可以直接参考隔离级别脏读不可重复读幻读实验观察方式READ UNCOMMITTED允许允许允许窗口B能读到窗口A未提交的修改READ COMMITTED禁止允许允许窗口B被阻塞直到窗口A提交REPEATABLE READ禁止禁止允许窗口B读过的行被加共享锁窗口A改不了SERIALIZABLE禁止禁止禁止窗口B的范围查询会锁住整个范围参数说明SQL Server 默认是 READ COMMITTEDMySQL InnoDB 默认是 REPEATABLE READ。实验里如果要求你验证「不可重复读」在 SQL Server 上需要把隔离级别降到 READ COMMITTED 以下才能观察到。这个差异在跨数据库实验里经常让人困惑。3.2 悲观锁和乐观锁在实验代码里长什么样锁的实验通常分两种悲观锁和乐观锁。悲观锁假设冲突一定会发生所以先锁住再说乐观锁假设冲突很少提交的时候再检查。悲观锁在 SQL Server 里最直接的写法是用 UPDLOCK 和 HOLDLOCK 提示-- 悲观锁示例在读取时就加更新锁防止其他事务修改 BEGIN TRANSACTION; SELECT Grade FROM SC WITH (UPDLOCK, HOLDLOCK) WHERE Sno 202100001 AND Cno C001; -- 此时其他事务想修改这行会被阻塞 UPDATE SC SET Grade Grade 5 WHERE Sno 202100001 AND Cno C001; COMMIT TRANSACTION;UPDLOCK 的意思是「我打算更新这行先占个更新锁」HOLDLOCK 相当于把隔离级别临时提升到 SERIALIZABLE锁会保持到事务结束。这两个提示组合起来就是典型的悲观锁策略。乐观锁在实验里通常用版本号字段来实现-- 乐观锁示例表里加一个 version 列 -- 先读取当前版本号 SELECT Grade, version FROM SC WHERE Sno 202100001 AND Cno C001; -- 假设读到的 version 3 -- 更新时检查版本号是否还是3 UPDATE SC SET Grade 95, version version 1 WHERE Sno 202100001 AND Cno C001 AND version 3; -- 检查 ROWCOUNT如果为0说明版本号已被别人改过 IF ROWCOUNT 0 PRINT 更新失败数据已被其他事务修改;乐观锁的关键在于 UPDATE 语句里的AND version 3这个条件。如果在这期间有别人改了这行version 变成 4那么这条 UPDATE 影响的行数就是 0程序就知道发生了冲突。实验报告里一般会要求你模拟两个会话同时读取、同时更新观察其中一个失败的情况。3.3 实验报告里事务部分怎么写才不被扣分事务实验的报告最容易犯的错是只贴代码不贴现象。老师要看的是你观察到了什么而不是你写了什么语句。我的建议是每个隔离级别至少记录三样东西两个窗口的操作顺序、每个时刻的查询结果、阻塞或报错的具体信息。比如验证「不可重复读」的时候窗口A先读一次窗口B修改并提交窗口A再读一次两次结果不一样。报告里要把两次读取的结果都截图或贴出来标注时间顺序。如果只是写「窗口A两次读取结果不同」没有具体数据这个实验等于没做。还有一个细节SQL Server 的默认隔离级别是 READ COMMITTED但实验指导书可能写的是「验证 READ COMMITTED 下的不可重复读」。这时候你需要确认——READ COMMITTED 确实允许不可重复读因为共享锁在读取完成后立即释放。如果你在实验里发现窗口A第二次读到的结果和第一次一样可能是窗口B的修改还没提交或者隔离级别设错了。排查的时候先查DBCC USEROPTIONS确认当前隔离级别。4. 课程设计从选题到答辩怎么把实验代码变成一份能交差的报告4.1 选题和需求分析阶段要定下来的三件事课程设计和实验的区别在于实验是验证性的课程设计是创造性的。你需要自己选一个业务场景从需求分析开始走完设计、实现、测试的全流程。第一件事是确定业务域。常见的选择有学生成绩管理、图书借阅、超市销售、酒店预订、医院挂号。选什么不重要重要的是业务逻辑要足够清晰能自然地引出多张表、多种关系和至少一个事务场景。比如图书借阅里「借书」这个操作就涉及库存表、借阅记录表、读者表而且需要事务保证借书和减库存同时成功或同时失败。第二件事是画 E-R 图并确定实体和联系。这一步决定了你后面要建几张表。实体通常对应一张表一对多联系可以在多端加外键多对多联系必须单独建一张关联表。很多人在这一步偷懒直接跳到建表结果做到一半发现关系没理清表结构要推倒重来。第三件事是确定功能模块。课程设计一般要求实现增删改查加至少一个复杂功能。复杂功能可以是存储过程、触发器、或者一个带事务的完整业务流程。建议在需求分析阶段就把这个复杂功能定下来不然后面容易做成一个纯 CRUD 的平庸项目。4.2 表结构设计和 SQL 脚本的组织方式表结构设计要遵循范式但实验环境里不必死磕到 3NF 或 BCNF。实际做课程设计时适度的反范式是可以接受的比如在订单表里冗余一个客户名称字段避免每次查询都 JOIN 客户表。关键是要在报告里说明为什么这么做以及可能带来的更新异常。SQL 脚本的组织建议按功能分文件而不是全部堆在一个文件里。我一般会这样分sql/ ├── 01_create_database.sql -- 建库 ├── 02_create_tables.sql -- 建表含约束 ├── 03_insert_data.sql -- 初始数据 ├── 04_views.sql -- 视图 ├── 05_procedures.sql -- 存储过程 ├── 06_triggers.sql -- 触发器 ├── 07_transactions.sql -- 事务示例 └── 08_queries.sql -- 查询示例这样分的好处是执行顺序清晰出错了也容易定位是哪个环节的问题。实验报告里贴代码的时候也按这个顺序贴老师看起来舒服你自己回头查也方便。建表的时候有几个参数需要特别注意。字符集统一用 UTF-8 或数据库默认不要混用日期时间字段根据精度要求选 DATE、DATETIME 还是 TIMESTAMP金额字段用 DECIMAL 而不是 FLOAT避免浮点误差。这些细节在报告里写一句「选用 DECIMAL(10,2) 保证金额精确到分」就是加分项。4.3 把实验代码改造成课程设计代码的实操路径如果你手里有一套实验代码想改成课程设计最省力的路径是保留表结构和基础查询替换业务场景和数据。具体操作先把实验里的 Student、Course、SC 三张表改成你选题对应的表。比如做图书管理就改成 Reader、Book、Borrow。字段名和类型可以参考实验但业务含义要换。然后保留实验里的视图、存储过程、触发器的结构把里面的表名和字段名替换掉。最后把查询示例改成你业务场景下的查询。这个改造过程中最容易出问题的地方是外键依赖。改了表名之后所有引用这些表的外键、视图、存储过程都要同步改。建议用编辑器的全局替换功能但替换之前先确认没有命名冲突。比如实验里有个字段叫 Sno你的新表里也有个字段叫 Sno 但含义不同全局替换就会出错。改造完成后从头到尾执行一遍所有脚本确保没有报错。然后跑几个典型查询确认结果符合预期。这一步做完你的课程设计骨架就有了剩下的就是补需求分析、E-R 图和测试记录。5. 避坑与排查复现这套实验时最容易翻车的五个地方5.1 外键约束导致建表和插入顺序报错现象执行建表脚本时SC 表创建失败报错「引用了无效的表 Student」。或者插入数据时往 SC 表插一条记录报错「外键约束冲突」。原因建表顺序不对先建了引用表再建被引用表。或者插入数据时子表的数据先于父表插入。解决建表按「被引用表 → 引用表」的顺序插入按「父表 → 子表」的顺序。如果已经建错了先删掉引用表再重建。删除时也要按相反顺序先删引用表再删被引用表。5.2 中文乱码和排序规则不匹配现象插入中文姓名后查询出来是问号或者乱码。或者跨数据库查询时报错「排序规则冲突」。原因建库或建表时没有指定支持中文的排序规则或者字段类型用了 VARCHAR 而不是 NVARCHAR。解决建库时指定COLLATE Chinese_PRC_CI_AS中文字段用 NVARCHAR 或 NCHAR。如果已经建好了用 ALTER 修改字段类型和排序规则。跨库查询时在 JOIN 或 WHERE 里显式指定 COLLATE。5.3 事务没有提交导致数据「消失」现象在窗口A插入或修改了数据窗口B查询不到。关闭窗口A后数据又出现了或者彻底消失了。原因窗口A开启了事务但没有 COMMIT数据还在事务日志里没有真正写入。窗口B在 READ COMMITTED 隔离级别下读不到未提交的数据。解决检查窗口A是否执行了 BEGIN TRANSACTION 但忘了 COMMIT。用DBCC OPENTRAN查看当前打开的事务。如果确认不需要保留执行 ROLLBACK 回滚。5.4 存储过程和触发器里的语法差异现象从 MySQL 迁移到 SQL Server 的存储过程代码直接报错比如DELIMITER不认识或者LIMIT不支持。原因不同 DBMS 的存储过程语法差异很大。MySQL 用 DELIMITER 改变语句结束符SQL Server 用 GO 分批。MySQL 支持 LIMITSQL Server 用 TOP 或 OFFSET FETCH。解决不要跨 DBMS 直接复制存储过程代码。先确认实验环境用的是哪个 DBMS然后按对应语法重写。SQL Server 的存储过程用CREATE PROCEDURE ... AS BEGIN ... END触发器用CREATE TRIGGER ... ON ... AFTER INSERT AS BEGIN ... END。5.5 实验报告里代码和结果对不上现象报告里贴的 SQL 代码和截图里的结果不一致比如代码查的是 C001 课程截图里显示的是 C002 的成绩。原因代码改了但截图没更新或者截图是之前版本的。这种情况在赶报告的时候特别常见。解决定稿前从头到尾重新执行一遍所有代码每执行一段就截一次图确保代码和结果一一对应。如果时间紧至少把核心功能的代码和结果重新对一遍。这个血泪经验值得记住——老师翻报告的时候第一眼看的就是代码和结果是否一致。6. 把实验代码变成自己能力的一个具体技巧最后说一个我自己用过的方法能把这套实验材料的价值放大好几倍。不要只是照着敲一遍、跑通、交报告而是每做完一个实验把代码关掉凭记忆重新写一遍。写不出来的时候再回去看看完再关掉重写。这个过程的痛苦程度和你的进步速度成正比。具体操作分三步。第一步做完实验后把 SQL 脚本全部关掉新建一个空文件从建库开始默写。能写多少写多少卡住了就标记一下继续往下写。第二步打开原脚本对照看看哪些地方写错了、哪些地方漏了。把错误分类是语法记错了还是逻辑没理清还是参数选错了。第三步针对错误最多的那类找三个类似的场景再练一遍。比如你总是搞混 LEFT JOIN 和 INNER JOIN就专门找五个查询需求每个都判断该用哪种 JOIN然后写出来验证。这个方法看起来笨但效果很实在。我当年做数据库实验的时候第一次默写建表语句连 PRIMARY KEY 的位置都写错了。练了三四次之后常用的 DDL 和 DML 基本能一气呵成。后来做课程设计建表环节几乎没卡过省下来的时间都花在业务逻辑和报告撰写上。还有一个习惯每套实验做完把报错信息和解决方法记在一个单独的文件里。不用写得很正式就记「什么操作 → 什么报错 → 怎么解决」。下次遇到类似问题先翻这个文件。积累到二三十条的时候你会发现大部分报错都是重复的排查速度会快很多。这个习惯我从实验课保持到工作到现在还在用。希望帮到你。本文还有配套的精品资源点击获取
返回列表