简介:这份资源是郑州大学计算机与人工智能学院《数据库系统原理》课程的实验报告文档,面向修读数据库课程的学生及需要完成DBMS实验的学习者,帮助解决从环境搭建到实验分析全流程的记录与总结问题。压缩包内共1个docx文件,约3.69MB,内容以实验报告正文为主,涵盖实验目的、内容、类型、学时分配及过程记录等模块。报告围绕万里数据库GreatSQL展开,涉及DBMS工作原理与系统构架认识、商用数据库的安装配置与启停管理、基本表视图触发器存储过程等逻辑组件、虚拟机CentOS环境配置、SELinux与防火墙关闭原因、依赖包安装及二进制包部署等实践环节,并强调实验截图编号、表格规范与结果分析反思的撰写要求。目前已有507人学习下载,适合需要参考实验流程、规范报告格式或搭建数据库实验平台的读者借鉴使用。
1. 数据库原理实验课设:从建库到事务,一份能跑通的完整路径
数据库原理这门课,实验报告是绕不过去的坎。很多同学理论课听得明白,一到动手就卡在环境配置、SQL 语法报错、事务隔离级别验证这几个环节上。这份实验报告的核心,是把关系模型、SQL 查询、完整性约束、事务与并发控制这几个模块串成一条可复现的链路。适合正在做数据库课设的本科生,也适合想系统梳理 SQL 实战的开发者。我按常见实验大纲拆成六个部分,每一步都给出可执行的 SQL 和参数说明,照着做能少走弯路。
2. 实验环境搭建与建库建表:先把地基打牢
2.1 数据库选型与安装要点
数据库原理实验通常要求使用关系型数据库,常见选择是 MySQL 或 PostgreSQL。MySQL 安装包体积小、社区资料多,适合课设快速上手;PostgreSQL 对标准 SQL 支持更严格,适合想深入理解约束和事务的同学。我一般推荐 MySQL 8.0 以上版本,因为窗口函数和 CTE 在复杂查询实验里会用到。
安装时注意字符集选 utf8mb4,排序规则用 utf8mb4_unicode_ci,避免中文乱码。端口默认 3306,如果本机已占用就改成 3307。安装完成后用命令行验证:
mysql -u root -p -e "SELECT VERSION();"这条命令会输出数据库版本号,确认服务已启动。如果报“Can't connect to MySQL server”,先检查服务是否运行,Windows 下在服务管理器里看 MySQL 服务状态,Linux 下用systemctl status mysql。
2.2 建库建表的完整 SQL 与字段设计
实验报告一般要求设计一个贴近实际的业务场景,比如学生选课系统、图书管理系统。我以学生选课为例,建三张表:学生表、课程表、选课表。先建库:
-- 创建数据库,指定字符集 CREATE DATABASE IF NOT EXISTS school_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; USE school_db;接着建学生表,主键用自增整数,学号加唯一约束:
CREATE TABLE student ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT '自增主键', sno VARCHAR(20) NOT NULL UNIQUE COMMENT '学号', sname VARCHAR(50) NOT NULL COMMENT '姓名', gender ENUM('M','F') DEFAULT 'M' COMMENT '性别', age INT CHECK (age BETWEEN 15 AND 60) COMMENT '年龄', major VARCHAR(50) COMMENT '专业', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;课程表和选课表:
CREATE TABLE course ( id INT AUTO_INCREMENT PRIMARY KEY, cno VARCHAR(20) NOT NULL UNIQUE COMMENT '课程号', cname VARCHAR(100) NOT NULL COMMENT '课程名', credit DECIMAL(3,1) CHECK (credit > 0 AND credit <= 10) COMMENT '学分', teacher VARCHAR(50) COMMENT '任课教师' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE sc ( id INT AUTO_INCREMENT PRIMARY KEY, sno VARCHAR(20) NOT NULL, cno VARCHAR(20) NOT NULL, score DECIMAL(5,2) CHECK (score >= 0 AND score <= 100) COMMENT '成绩', semester VARCHAR(20) COMMENT '学期', UNIQUE KEY uk_sno_cno (sno, cno), FOREIGN KEY (sno) REFERENCES student(sno) ON DELETE CASCADE, FOREIGN KEY (cno) REFERENCES course(cno) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里有几个参数值得说明。ENGINE=InnoDB是必须的,因为只有 InnoDB 支持事务和外键,MyISAM 不支持,做事务实验会翻车。CHECK约束在 MySQL 8.0.16 之前是被忽略的,如果版本低,年龄和成绩的范围校验要放到应用层做。ON DELETE CASCADE表示删除学生时自动删除其选课记录,实验里验证级联删除会用到。
2.3 插入测试数据与验证约束
插入数据时故意制造几条违反约束的记录,观察报错信息,这是实验报告里“完整性验证”部分的素材:
-- 正常插入 INSERT INTO student (sno, sname, gender, age, major) VALUES ('2021001', '张三', 'M', 20, '计算机'), ('2021002', '李四', 'F', 21, '软件工程'), ('2021003', '王五', 'M', 19, '计算机'); INSERT INTO course (cno, cname, credit, teacher) VALUES ('C001', '数据库原理', 3.0, '赵老师'), ('C002', '操作系统', 3.5, '钱老师'), ('C003', '计算机网络', 2.5, '孙老师'); INSERT INTO sc (sno, cno, score, semester) VALUES ('2021001', 'C001', 88.5, '2023-2024-1'), ('2021001', 'C002', 76.0, '2023-2024-1'), ('2021002', 'C001', 92.0, '2023-2024-1'), ('2021003', 'C003', 85.0, '2023-2024-1');验证唯一约束:重复插入sno='2021001'会报Duplicate entry。验证外键约束:插入sno='9999999'到 sc 表会报Cannot add or update a child row。验证 CHECK 约束:插入age=10会报Check constraint 'student_chk_1' is violated。这些报错信息截图放进实验报告,比只写“约束生效”有说服力得多。
3. SQL 查询实验:从单表到多表连接的递进写法
3.1 单表查询与聚合函数的典型组合
单表查询实验通常要求覆盖 WHERE、ORDER BY、GROUP BY、HAVING、LIMIT 这几个子句。我习惯用一条综合查询把大部分语法串起来:
-- 查询每个专业的学生人数和平均年龄,只显示人数大于1的专业 SELECT major, COUNT(*) AS student_count, ROUND(AVG(age), 1) AS avg_age FROM student WHERE gender IS NOT NULL GROUP BY major HAVING COUNT(*) > 1 ORDER BY student_count DESC LIMIT 10;执行顺序是 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT。WHERE 过滤行,HAVING 过滤分组,这是实验报告里常考的点。ROUND(AVG(age), 1)保留一位小数,避免结果太长。如果 WHERE 里写聚合函数会报Invalid use of group function,这是新手最容易踩的坑。
3.2 多表连接:INNER JOIN、LEFT JOIN 的差异验证
多表连接是实验重点。用 INNER JOIN 查选课详情:
SELECT s.sno, s.sname, c.cname, sc.score FROM student s INNER JOIN sc ON s.sno = sc.sno INNER JOIN course c ON sc.cno = c.cno WHERE sc.score >= 80 ORDER BY sc.score DESC;再用 LEFT JOIN 对比,找出没选任何课的学生:
SELECT s.sno, s.sname, sc.cno FROM student s LEFT JOIN sc ON s.sno = sc.sno WHERE sc.cno IS NULL;INNER JOIN 只返回两表都匹配的行,LEFT JOIN 保留左表全部行,右表无匹配时填 NULL。实验报告里要写清楚这个差异,最好用同一组数据跑两种连接,把结果集截图对比。如果 LEFT JOIN 后 WHERE 条件写在 ON 里和写在 WHERE 里结果不同,这是另一个考点:ON 里的条件在连接时过滤,WHERE 里的条件在连接后过滤。
3.3 子查询与 EXISTS 的适用场景
子查询实验一般要求用 IN、EXISTS、ANY、ALL 各写一条。查选了“数据库原理”的学生:
-- 用 IN 写 SELECT sno, sname FROM student WHERE sno IN ( SELECT sc.sno FROM sc INNER JOIN course c ON sc.cno = c.cno WHERE c.cname = '数据库原理' ); -- 用 EXISTS 写 SELECT s.sno, s.sname FROM student s WHERE EXISTS ( SELECT 1 FROM sc INNER JOIN course c ON sc.cno = c.cno WHERE sc.sno = s.sno AND c.cname = '数据库原理' );IN 适合子查询结果集小的情况,EXISTS 适合相关子查询,外层表大时效率更高。实验报告里可以写一句“在本数据集下两者执行计划相同,但数据量增大后 EXISTS 可利用索引提前终止”。查所有课程成绩都大于 80 的学生用 NOT EXISTS 或 ALL:
SELECT s.sno, s.sname FROM student s WHERE NOT EXISTS ( SELECT 1 FROM sc WHERE sc.sno = s.sno AND sc.score <= 80 );这条查询的逻辑是“不存在任何一门成绩小于等于 80 的选课记录”,等价于“所有成绩都大于 80”。NOT EXISTS 比 NOT IN 安全,因为 NOT IN 遇到 NULL 会返回空结果集,这是血泪经验。
4. 事务与并发控制实验:隔离级别的现象验证
4.1 事务的 ACID 与手动控制语句
事务实验要求验证原子性、一致性、隔离性、持久性。MySQL 默认自动提交,要手动控制事务需要先关闭自动提交:
SET autocommit = 0; START TRANSACTION; UPDATE sc SET score = score + 5 WHERE sno = '2021001' AND cno = 'C001'; -- 此时另开一个会话查询,看不到未提交的修改 ROLLBACK; -- 回滚后分数恢复原值验证原子性:在事务里执行两条 UPDATE,第二条故意写错表名,整个事务回滚,第一条也不生效。验证持久性:COMMIT 之后重启数据库服务,数据仍在。实验报告里要写清楚每个步骤的预期现象和实际现象。
4.2 四种隔离级别的设置与现象复现
MySQL 支持四种隔离级别,用一条命令查看和设置:
-- 查看当前隔离级别 SELECT @@transaction_isolation; -- 设置为读未提交 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 设置为读已提交 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 设置为可重复读 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 设置为串行化 SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;复现脏读:会话 A 设为 READ UNCOMMITTED,开启事务修改分数但不提交;会话 B 同样设为 READ UNCOMMITTED,查询能看到未提交的分数。复现不可重复读:会话 A 设为 READ COMMITTED,开启事务第一次查询;会话 B 修改并提交;会话 A 第二次查询结果不同。复现幻读:会话 A 设为 REPEATABLE READ,第一次查询某范围记录;会话 B 插入新记录并提交;会话 A 第二次查询仍看不到新记录,但 UPDATE 该范围会影响到新记录,这就是幻读。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 不可能 | 可能 | 可能 |
| REPEATABLE READ | 不可能 | 不可能 | InnoDB 下基本不可能 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 |
这张表是实验报告的核心结论,但一定要自己跑一遍再填,不同数据库版本行为有差异。
4.3 锁机制与死锁排查
InnoDB 的行锁实验:会话 A 对某行加排他锁,会话 B 修改同一行会阻塞。查看锁等待:
-- 查看当前锁等待情况 SELECT * FROM performance_schema.data_lock_waits; -- 查看 InnoDB 状态,包含最近一次死锁信息 SHOW ENGINE INNODB STATUS;制造死锁:会话 A 锁行 1 再请求行 2,会话 B 锁行 2 再请求行 1,双方互相等待,InnoDB 检测到后回滚其中一个事务并报Deadlock found。实验报告里把SHOW ENGINE INNODB STATUS输出的 LATEST DETECTED DEADLOCK 段落截图,分析哪个事务被回滚、为什么。这个部分能体现对锁的理解深度。
5. 实验报告避坑:那些让分数打折的细节
5.1 环境不一致导致 SQL 报错
现象:本地跑通的 SQL 换到实验室机器上报语法错误。原因:MySQL 版本不同,8.0 支持窗口函数和 CTE,5.7 不支持。解决:实验报告开头写明数据库版本,用SELECT VERSION()确认;如果实验室是 5.7,把窗口函数改成子查询或自连接实现。
5.2 外键约束导致插入顺序错误
现象:先插 sc 表报Cannot add or update a child row。原因:外键要求被引用表中已存在对应记录。解决:按 student → course → sc 的顺序插入;或者临时SET FOREIGN_KEY_CHECKS = 0关闭检查,插完再打开,但实验报告里不推荐这种做法,会掩盖约束验证环节。
5.3 事务实验忘记关闭自动提交
现象:ROLLBACK 之后数据没恢复。原因:autocommit 默认为 1,每条 SQL 自动提交,ROLLBACK 无效。解决:SET autocommit = 0或显式START TRANSACTION。注意START TRANSACTION会隐式提交前一个事务,实验时每个事务单独开。
5.4 隔离级别设置用错作用域
现象:设了 SESSION 级别但另一个会话没生效。原因:SESSION 只影响当前连接,GLOBAL 影响新连接但不影响已存在的连接。解决:两个会话都设置,或者用SET GLOBAL后重新连接。实验报告里写清楚设置的是 SESSION 还是 GLOBAL。
5.5 中文乱码与排序规则冲突
现象:插入中文显示问号。原因:建库时没指定 utf8mb4,或者连接字符集不对。解决:建库建表都指定DEFAULT CHARSET=utf8mb4,连接串加?characterEncoding=utf8。如果已经建错,用ALTER DATABASE school_db CHARACTER SET utf8mb4;修改,但已有数据可能需要重新导入。
6. 进阶技巧:用执行计划和索引优化给实验报告加分
实验报告如果只停留在“能跑通”,分数上限有限。加一段执行计划分析,能体现对查询优化的理解。用 EXPLAIN 看查询走没走索引:
EXPLAIN SELECT s.sname, c.cname, sc.score FROM student s INNER JOIN sc ON s.sno = sc.sno INNER JOIN course c ON sc.cno = c.cno WHERE sc.score > 85;重点看 type 列:ALL 是全表扫描,ref 是索引查找,eq_ref 是唯一索引查找,range 是范围扫描。key 列显示实际使用的索引,rows 列是预估扫描行数。如果 sc 表的 score 字段没索引,type 会是 ALL,给 score 加索引后再看:
CREATE INDEX idx_score ON sc(score);再跑一次 EXPLAIN,type 变成 range,rows 明显减少。实验报告里把前后对比贴出来,写一句“索引将全表扫描优化为范围扫描,预估行数从 N 降到 M”。注意索引不是越多越好,写操作频繁的表加索引会拖慢插入和更新,这是取舍。
另一个加分项是用窗口函数做排名查询,MySQL 8.0 支持:
SELECT sno, cno, score, RANK() OVER (PARTITION BY cno ORDER BY score DESC) AS rk, AVG(score) OVER (PARTITION BY cno) AS avg_score FROM sc;这条查询按课程分组,给每个学生的成绩排名,同时算出该课程平均分。窗口函数不减少行数,和 GROUP BY 的区别要写清楚。如果实验室是 MySQL 5.7,用自连接实现排名:
SELECT s1.sno, s1.cno, s1.score, (SELECT COUNT(*) FROM sc s2 WHERE s2.cno = s1.cno AND s2.score > s1.score) + 1 AS rk FROM sc s1;这个写法在数据量大时性能差,但能体现对相关子查询的理解。我一般建议实验报告里两种都写,对比可读性和执行效率。
最后说一个习惯:每做完一个实验模块,把 SQL 脚本按模块存成独立文件,命名用01_create.sql、02_query.sql、03_transaction.sql,实验报告附录里直接贴文件内容。这样答辩时老师问哪个模块,你能快速定位。数据库实验最怕的就是脚本散落各处,最后自己都找不到哪条 SQL 对应哪个结论。希望帮到你。
本文还有配套的精品资源,点击获取