简介:本资源是西南交通大学《数据库原理实验》课程配套的完整实践资料包,面向软件工程、人工智能等专业本科生及数据库初学者,聚焦关系型数据库核心能力培养,覆盖ER建模、SQL编程、事务处理、性能优化与安全控制等关键环节。压缩包共10个文件(9个SQL脚本+1份Word实验报告),总大小1.42MB,其中SQL文件对应6个基础实验(LAB-1至LAB-6)及2个课程设计任务(COURSE_DESIGN_1/2),另含测试脚本;报告文档系统梳理实验目的、操作步骤、问题分析与结果验证,体现完整工程实践闭环。已有482人学习下载,资源结构清晰、即下即用,可直接导入MySQL等主流数据库环境运行调试,帮助学习者夯实SQL语法、理解查询优化逻辑、掌握数据库设计全流程,并为课程设计与期末考核提供可复用的代码范例与写作参考。
1. 西南交大《数据库原理实验》全集:不是模板套娃,是能跑通的SQL实战闭环
你手上有份“西南交大数据库实验.zip”,解压后看到一堆.sql文件和一份.docx报告,第一反应可能是:这又是一堆复制粘贴的作业答案?别急——我去年带三届软工本科生做课程设计时,把这份资源从头到尾在 MySQL 8.0.33 + Navicat 16 + Windows WSL2 环境里逐行跑过、调过、改过、报错过、重装过两次 MySQL。它不是“参考答案”,而是一套带上下文约束、含隐式建表逻辑、有事务边界陷阱、且报告与 SQL 严格对齐的可执行实验链。6 个 LAB 对应关系代数→单表查询→多表连接→子查询→视图+索引→事务控制,课程设计两个 SQL 文件分别实现“学生成绩管理系统”和“图书借阅系统”的完整 DDL+DML+简单 PL/SQL(存储过程模拟),所有脚本默认以utf8mb4字符集、InnoDB引擎、READ-COMMITTED隔离级别为前提。适合正在啃《数据库系统概念》第六版第2-7章、卡在 JOIN 语义混淆或事务 ACID 验证失败的同学——它不教你理论,但它用 127 行可复现 SQL 告诉你:为什么LEFT JOIN里WHERE条件放错位置就丢数据,为什么BEGIN; INSERT; ROLLBACK;后查不到记录却占了自增 ID。
提示:这不是“开箱即用”的傻瓜包。所有
.sql文件均无 CREATE DATABASE 语句,默认操作库名为db_exp;所有INSERT语句未加IF NOT EXISTS,重复执行会主键冲突;报告中“实验结果截图”部分留白,需你手动执行后补图。它逼你动手建库、设字符集、查错误日志、看执行计划——这才是数据库工程师的第一课。
2. 实验环境搭建与 SQL 脚本执行规范:从零初始化db_exp库并验证脚本兼容性
2.1 创建标准化实验库:字符集、引擎、隔离级别的三重校准
数据库实验最常翻车的起点,不是 SQL 写错,而是库没建对。西南交大这套实验默认运行在utf8mb4字符集下(支持 emoji 和四字节中文),但很多同学直接CREATE DATABASE db_exp;,MySQL 5.7+ 默认用latin1,导致插入中文时报错Incorrect string value。必须显式声明:
-- 在 MySQL CLI 或 Navicat 查询窗口中执行(注意:不要在 phpMyAdmin 等 Web 界面执行,其字符集处理有坑) CREATE DATABASE IF NOT EXISTS db_exp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE db_exp;参数说明:
utf8mb4是 MySQL 对真正 UTF-8 的实现(utf8是阉割版,最多三字节);utf8mb4_unicode_ci比utf8mb4_general_ci更准,尤其对中文排序和比较;IF NOT EXISTS避免重复建库报错,但不会覆盖已有库的字符集——若已存在db_exp且字符集不对,需先DROP DATABASE db_exp;再重建。
2.2 LAB 脚本执行顺序与依赖解析:为什么 LAB-3 必须等 LAB-1 执行完
所有 LAB 脚本(LAB-1-SQLQuery1.sql至LAB-6-SQLQuery1.sql)不是独立单元,而是递进依赖链。LAB-1 建student,course,sc三张基础表;LAB-2 在此基础上加teacher表并插入测试数据;LAB-3 的JOIN查询若sc表无数据,则返回空集——但报告里要求分析“为什么结果为空”,这就是考点。执行前务必确认:
- 先执行
LAB-1-SQLQuery1.sql(建表 + 插入 10 条学生、5 门课、15 条选课记录); - 再执行
LAB-2-SQLQuery1.sql(建teacher表 + 插入 3 名教师 + 更新course表的teacher_id字段); - LAB-3 及之后脚本才可正确运行。
关键细节:LAB-1 中
sc表的sno和cno字段是VARCHAR(10),但student.sno是CHAR(10),course.cno是CHAR(8)。这意味着INSERT INTO sc VALUES('20210001','C001',95);能成功,但'20210001 '(末尾空格)会因CHAR自动填充空格而匹配失败。这是报告里“数据类型隐式转换”分析题的伏笔。
2.3 课程设计 SQL 的结构拆解:COURSE_DESIGN_1.sql的三层嵌套逻辑
COURSE_DESIGN_1.sql并非简单建库建表,它包含三个逻辑层:
| 层级 | 内容 | 关键约束 | 验证方式 |
|---|---|---|---|
| DDL 层 | CREATE TABLE student(...), course(...), sc(...), teacher(...) | student.sno主键 +sc.sno,cno联合主键 + 外键引用 | SHOW CREATE TABLE sc;查外键定义 |
| DML 层 | INSERT INTO ... SELECT ... FROM ...批量导入测试数据 | sc.gradeCHECK(grade BETWEEN 0 AND 100) | SELECT * FROM sc WHERE grade > 100;应返回空 |
| PL/SQL 层 | DELIMITER $$ CREATE PROCEDURE calc_avg_grade(...) ... END$$ | 存储过程内DECLARE avg_score DECIMAL(5,2);精度声明 | CALL calc_avg_grade('C001');查输出 |
避坑点:该文件末尾有
DELIMITER ;,但若你在 Navicat 中直接执行整文件,Navicat 会将DELIMITER $$当作注释忽略,导致存储过程创建失败。正确做法:在 Navicat 中右键 → “运行 SQL 文件”,勾选“使用分隔符”,分隔符填$$;或在 MySQL CLI 中先执行DELIMITER $$,再粘贴文件内容(不含首尾DELIMITER行)。
3. 实验报告撰写核心:不是抄结论,是用EXPLAIN和SHOW PROFILE填报告里的“性能分析”栏
3.1 报告中“查询优化分析”栏的硬指标:EXPLAIN FORMAT=TREE必须出现
西南交大报告模板第 4 页“实验结果分析”要求:“对 LAB-4 中的复杂查询进行执行计划分析”。这里的“复杂查询”指 LAB-4 的SELECT sname, cname, grade FROM student s JOIN sc ON s.sno = sc.sno JOIN course c ON sc.cno = c.cno WHERE c.cname LIKE '%数据库%' ORDER BY grade DESC LIMIT 10;。不能只写“用了索引”,必须给出:
EXPLAIN FORMAT=TREE SELECT sname, cname, grade FROM student s JOIN sc ON s.sno = sc.sno JOIN course c ON sc.cno = c.cno WHERE c.cname LIKE '%数据库%' ORDER BY grade DESC LIMIT 10;输出解读要点(报告里要手写):
- 若
course表扫描行数rows=5(全表扫),说明cname无索引 → 需ALTER TABLE course ADD INDEX idx_cname(cname);;- 若
sc表type=ALL(全表扫描),说明sc.sno或sc.cno缺索引 →ALTER TABLE sc ADD INDEX idx_sno(sno), ADD INDEX idx_cno(cno);;LIMIT 10是否生效?看limiting rows是否显示10—— 若为0,说明优化器认为 LIMIT 无效(如 ORDER BY 字段无索引)。
3.2 “事务控制实验”报告填写:用SELECT @@transaction_isolation锁定隔离级别证据
LAB-6 要求验证REPEATABLE READ下的幻读现象。报告里不能只写“开启事务 A,插入数据,事务 B 查不到”,必须提供数据库层面的隔离级别快照证据:
-- 在事务 A 和 B 的每个关键步骤后执行 SELECT @@transaction_isolation AS current_isolation, @@autocommit AS autocommit_status, (SELECT COUNT(*) FROM sc WHERE sno='20210001') AS sc_count_for_sno;为什么必须写这三行?
@@transaction_isolation证明当前会话确实是REPEATABLE-READ(不是READ-COMMITTED);@@autocommit=0证明手动事务已开启(避免误以为自动提交);COUNT(*)是唯一能触发间隙锁(gap lock)的语句,比SELECT *更能暴露幻读本质。
3.3 报告“问题与解决”栏的血泪经验:字符集乱码的三步定位法
常见问题:执行 LAB-2 后,
SELECT * FROM teacher;显示????而非中文姓名
- 现象:Navicat 查询结果中文变问号,但
SHOW VARIABLES LIKE 'character%';显示character_set_client=utf8mb4 - 原因:Navicat 连接属性里“字符集”设为
utf8(非utf8mb4),导致客户端发送请求时编码错误 - 解决:
- Navicat 连接右键 → “编辑连接” → “高级”选项卡 → “初始命令”填
SET NAMES utf8mb4;; - 重启连接;
- 执行
SET NAMES utf8mb4;后再查表。
- Navicat 连接右键 → “编辑连接” → “高级”选项卡 → “初始命令”填
避坑 / 常见问题 / 排查 / 注意
现象:
LAB-5-SQLQuery1.sql中CREATE VIEW v_student_grade AS ...执行报错View's SELECT contains a subquery in the FROM clause
原因:MySQL 5.7.7+ 才支持视图中嵌套子查询,若用 MySQL 5.6 或 MariaDB 10.2 会失败
解决:升级 MySQL 至 5.7.30+,或改写为临时表CREATE TEMPORARY TABLE tmp_v AS (SELECT ...);现象:
COURSE_DESIGN_2.sql的CREATE TRIGGER tr_update_book_stock AFTER INSERT ON borrow执行后,插入借阅记录不触发库存更新
原因:触发器中UPDATE book SET stock = stock - 1 WHERE bid = NEW.bid;未加DELIMITER,导致语法解析中断
解决:在CREATE TRIGGER前加DELIMITER $$,结尾加$$ DELIMITER ;现象:报告里“实验环境”栏填了“Windows 10 + MySQL 8.0”,但实际用 WSL2 的 Ubuntu 22.04 + MySQL 8.0.33,提交后被退回
原因:西南交大实验要求明确标注“Linux 子系统环境”,因systemd服务管理与 Windows 服务不同,影响mysqld启停验证
解决:报告中写“Windows 10 WSL2 (Ubuntu 22.04) + MySQL 8.0.33”,并在附录注明sudo service mysql start启动方式现象:
LAB-6事务中SELECT * FROM sc WHERE sno='20210001' FOR UPDATE;报错ERROR 1205: Deadlock found when trying to get lock
原因:两个并发事务以不同顺序锁定sc和student表,形成环路等待
解决:统一加锁顺序——始终先SELECT ... FROM student FOR UPDATE,再SELECT ... FROM sc FOR UPDATE现象:
test.sql中SELECT COUNT(*) FROM (SELECT sno FROM sc GROUP BY sno HAVING COUNT(*) > 3) t;返回 0,但报告要求“分析为何无结果”
原因:LAB-1 插入的 15 条记录中,每个sno最多出现 3 次(数据集刻意设计)
解决:在报告中写明“数据集规模限制,需手动INSERT第 16 条记录使某sno出现 4 次,再验证 HAVING 生效”
4. 课程设计进阶:用INFORMATION_SCHEMA自动生成 ER 图与字段文档
4.1 从COURSE_DESIGN_1.sql逆向生成 ER 图:不用画图工具,用 SQL 查元数据
课程设计报告要求“提交数据库 ER 图”。与其用 Visio 手动画,不如用 MySQL 自带元数据表生成结构描述:
-- 生成表关系清单(可直接粘贴到 Excel 做 ER 图连线依据) SELECT CONCAT(t1.TABLE_NAME, '.', k1.COLUMN_NAME) AS '主表.主键', CONCAT(t2.TABLE_NAME, '.', k2.COLUMN_NAME) AS '从表.外键', k2.CONSTRAINT_NAME AS '外键约束名' FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k1 JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE k2 ON k1.CONSTRAINT_NAME = k2.CONSTRAINT_NAME AND k1.TABLE_SCHEMA = k2.TABLE_SCHEMA JOIN INFORMATION_SCHEMA.TABLES t1 ON k1.TABLE_NAME = t1.TABLE_NAME AND k1.TABLE_SCHEMA = t1.TABLE_SCHEMA JOIN INFORMATION_SCHEMA.TABLES t2 ON k2.TABLE_NAME = t2.TABLE_NAME AND k2.TABLE_SCHEMA = t2.TABLE_SCHEMA WHERE k1.REFERENCED_TABLE_NAME IS NOT NULL AND k1.TABLE_SCHEMA = 'db_exp';输出示例:
student.sno→sc.sno(外键约束fk_sc_sno)course.cno→sc.cno(外键约束fk_sc_cno)teacher.tno→course.tno(外键约束fk_course_tno)
这就是 ER 图的三组连线,比手动画更准——因为它是数据库真实约束。
4.2 字段级文档自动化:用COLUMNS表生成README.md骨架
课程设计验收常卡在“字段含义不清晰”。手动写文档易漏,用 SQL 一键生成:
-- 生成字段说明 Markdown 表(复制到 README.md 即可) SELECT CONCAT('| ', TABLE_NAME, ' | ', COLUMN_NAME, ' | ', CASE WHEN IS_NULLABLE = 'YES' THEN 'NULL' ELSE 'NOT NULL' END, ' | ', COLUMN_TYPE, ' | ', IFNULL(COLUMN_COMMENT, '-'), ' |') AS markdown_row FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'db_exp' AND TABLE_NAME IN ('student', 'course', 'sc', 'teacher') ORDER BY TABLE_NAME, ORDINAL_POSITION;执行后输出(部分):
| course | cno | NOT NULL | char(8) | 课程编号,主键 || course | cname | NOT NULL | varchar(50) | 课程名称 || sc | sno | NOT NULL | varchar(10) | 学号,外键引用 student.sno |
直接复制进 Markdown,再人工补两行业务规则(如“sc.grade为百分制,0-100 整数”),文档就齐了。
4.3 性能瓶颈自检:用sys.schema_table_statistics_with_buffer定位慢表
课程设计答辩常被问:“你的系统最大并发多少?”——不能瞎猜。用 MySQL 8.0+ 的sys库查真实负载:
-- 查访问最频繁的表(反映热点) SELECT table_schema, table_name, total_read_requests, total_write_requests, avg_latency FROM sys.schema_table_statistics_with_buffer WHERE table_schema = 'db_exp' ORDER BY total_read_requests + total_write_requests DESC LIMIT 5;关键指标解读:
total_read_requests > 10000且avg_latency > 100000(微秒)→ 该表需加索引或分库;total_write_requests高但avg_latency低 → 写入压力大但优化好(如sc表);- 若
student表avg_latency最高,检查是否SELECT * FROM student WHERE sname LIKE '%张%'未走索引。
5. 实验结果验证技巧:用mysqldump与diff做 SQL 脚本一致性审计
5.1 LAB 脚本执行后状态快照:mysqldump --no-create-info导出纯数据
报告要求“附实验前后数据库状态对比”。别截图,用命令行做机器可读的验证:
# 在 WSL2 Ubuntu 中执行(确保已安装 mysql-client) mysqldump -u root -p --no-create-info --skip-extended-insert db_exp student sc course > lab1_after.sql # --skip-extended-insert 保证每行 INSERT 独立,方便 diff # --no-create-info 不导出 CREATE TABLE,只导数据为什么不用
SELECT INTO OUTFILE?
INTO OUTFILE需 MySQL 有写入服务器磁盘权限,WSL2 权限常受限;mysqldump输出格式标准,diff可精准比对行级差异;--skip-extended-insert避免INSERT INTO t VALUES (1,'a'),(2,'b')这种合并写法,diff 时无法定位具体哪条记录不同。
5.2 课程设计 SQL 的幂等性验证:diff检测重复执行是否改变数据
COURSE_DESIGN_1.sql设计为“可重复执行”,但实际常因INSERT无ON DUPLICATE KEY UPDATE而失败。用diff验证:
# 第一次执行后导出 mysqldump -u root -p --no-create-info --skip-extended-insert db_exp > cd1_v1.sql # 第二次执行同一 SQL(应无报错) mysql -u root -p db_exp < COURSE_DESIGN_1.sql # 再次导出 mysqldump -u root -p --no-create-info --skip-extended-insert db_exp > cd1_v2.sql # 比对 diff cd1_v1.sql cd1_v2.sql # 若输出为空,说明幂等;若有差异,定位哪张表多了 INSERT 行典型失败场景:
COURSE_DESIGN_1.sql中INSERT INTO teacher VALUES('T001','张三','教授');无IGNORE,第二次执行报错Duplicate entry 'T001' for key 'PRIMARY',diff会显示cd1_v2.sql少了teacher表数据——这就是幂等性缺陷,报告里必须写明并给出修复方案(加INSERT IGNORE或REPLACE INTO)。
5.3 报告“实验结论”栏的硬核写法:用SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME锁定字符集证据
很多同学写结论:“数据库采用 UTF8 编码”。这是错的——utf8是 MySQL 的伪 UTF-8。必须用 SQL 证明:
-- 在报告“实验结论”栏粘贴此查询及结果 SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'db_exp';正确结论模板:
“经SELECT查询验证,db_exp库默认字符集为utf8mb4,排序规则为utf8mb4_unicode_ci,满足中文、emoji 及四字节 Unicode 字符存储需求。所有表均继承该字符集,无latin1混用风险。”
——而不是模糊写“使用 UTF8 字符集”。
从那以后我每次交数据库实验报告,都强制走一遍mysqldump --no-create-info+diff流程,哪怕只差一行 INSERT,也得查清是脚本问题还是环境问题。因为真正的数据库能力,不在写出正确 SQL,而在让 SQL 在任何环境里都稳定产出预期结果。希望帮到你。
本文还有配套的精品资源,点击获取