简介这是一份郑州大学《数据库系统原理实验》课程配套实验报告书面向计算机科学与技术、软件工程专业的本科生系统覆盖数据库从入门到进阶的完整实践环节。报告以openGauss为实验平台依次记录认识DBMS系统、创建数据库/表/索引、交互式SQL语句、视图、完整性控制、安全性控制、事务与并发控制、备份与恢复及JDBC连接数据库九个实验每个实验都包含操作过程、问题修正、结果分析与截图说明。资源为单个docx文档3.23MB共1个文件排版规范目录清晰便于直接参考或按需修改。目前已有1520人学习使用适合正在学习数据库原理、需要完成实验报告或备考复习的同学借鉴既能帮助理解概念模型与关系模型也可快速上手openGauss的数据库定义、查询与管理操作。1. 数据库实验报告书不是交差用的流水账而是一份别人照着跑就能复现的验收材料每到结课周总有人对着zzu数据库实验报告书这个命名发呆——它可能是课程组发的模板、学长留下的压缩包也可能是你自己给文件起的名字。所谓实验报告书本质是把一次完整的数据库实验过程固化成文档从需求描述、ER 图与关系模式设计到建库建表、增删改查、事务与备份恢复每一步都要有可验证的痕迹。一句话说清楚它的价值别人拿到这份材料按顺序执行里面的 SQL 附件能还原出和你完全一样的数据库状态。这里有个反直觉的结论报告书写得好不好关键不在「写」而在「跑」。一份满是概念、但没有一条能还原实验结果的 SQL 的报告书在答辩和批改面前基本站不住脚反过来代码全部跑通、截图与输出一一对应、坑和取舍都写清楚的报告书哪怕篇幅短一点也比注水的强。这份材料的读者很明确正在被数据库课程实验或课程设计缠住的本科生、需要按规范验收学生的老师以及想借一套可复用模板快速搭出交付物的从业者。下面按我写这类报告书的习惯把从结构到落地、从踩坑到答辩的完整路径拆给你。2. 报告书六个模块怎么排先定实验边界再写设计文档最后补运行记录一份数据库实验报告书最早被吐槽的就是「想到哪写到哪」。要么开头堆一大段数据库发展史要么直接上代码让人看不懂表之间什么关系。我一般把报告书拆成六个模块任务与目标、环境与运行说明、概念设计、逻辑设计、物理实现、运行与验证。这个顺序本身就是一个标准的数据库开发流程照着排不会乱。模块要写什么评审关心什么任务与目标实验题目、数据规模、功能边界工作量是否真实题目理解是否准确环境与运行说明数据库版本、操作系统、连接方式、初始化步骤能不能按说明把环境复现出来概念设计ER 图、实体与联系说明实体和联系是否理清多对多是否被正确拆掉逻辑设计关系模式、范式分析、命名约定主外键是否合理有没有冗余更新异常物理实现建表语句、索引、视图、存储过程、触发器SQL 能不能跑通约束是否按设计落实运行与验证增删改查结果截图、关键查询输出、事务回滚演示实验结果和设计是否一致异常处理是否分析过2.1 从任务书里抠出实验边界环境、数据规模、验收点拿到实验题目别急着开 Navicat先把边界划清楚。实验报告书里最常见的问题就是用了老师没要求的功能或者反过来核心要求漏了。我习惯先列一张验收点核对表把题目里的硬性要求逐条抄出来。比如「要求使用 MySQL 8.0」「数据量不少于 500 条」「包含一个视图和两个存储过程」「演示事务回滚」。这些词汇就是实验的验收点后续每个模块都要能对应到至少一条验收点。划完边界要落成一句话的实验目标。比如「完成一个学生选课系统的数据库设计支持课程查询、选课、退课、成绩录入与统计并演示事务与并发控制」。这句话会贯穿后面的 ER 图和建表语句写乱了就回来对照它。这个阶段还要决定一件事实验数据库是用课程组提供的现成库还是自己从零设计。前者省时间但报告书里设计分析部分会显得薄后者更完整对应的工作量也更大。我的建议是如果题目没有明确指定自己设计数据表哪怕简单一些因为概念设计和逻辑设计两章才有东西可写。2.2 设计文档怎么写ER 图、关系模式、范式与命名约定设计文档是报告书的灵魂部分但很多人把 ER 图画成了「数据库表结构截图」这是两回事。ER 图强调实体、属性和联系学生和课程之间的选课是多对多联系成绩是联系上的属性而不是某张表的普通字段。画完 ER 图要转成关系模式这一步有固定的映射规则可以直接抄进报告书。ER 元素关系模式转换规则实体每个实体一张表实体属性成为字段一对一联系任选一端表加外键一对多联系在多端表加外键多对多联系单独拆一张中间表中间表包含双方主键作为复合主键联系属性放到中间表或外键所在表中范式分析不用长篇大论。重点写清楚第一范式保证字段不可再分第二范式要求非主属性完全依赖复合主键第三范式要求消除传递依赖。以学生选课为例把成绩字段设计在选课表里而不是学生表里就是第三范式的体现反过来如果选的中间表还存了课程名称就构成传递依赖要扣掉。命名约定是另一个容易被忽视的点。我一般统一用 snake_case表名单数字段用小写带下划线主键写成表名单数_id或直接用业务编号。写报告书时把COMMENT注释加在每个字段后面后面复制到 SHOW CREATE TABLE 输出里就是现成的字段说明省得再手写一遍表结构说明。这一步做得越细后续写物理实现章节越省力。3. 跑通报告书里的最小命令链建库、建表、增删改查与事务一条命令都不浪费设计文档再漂亮最终要落到能跑的 SQL 上。我这部分会按「环境配置 → 建库建表 → 增删改查与事务」的顺序推一遍命令每一条都是报告书里可以直接复用的最小可用版本。这里的核心原则是每条命令写清楚为什么这么写、参数代表什么、失败了看哪条错误信息。3.1 数据库环境选型MySQL 8.0 是默认选项人大金仓与达梦要注意三点兼容差异实验报告书的环境章不需要唯品牌论但要交代清楚自己用什么、为什么。高校实验环境里最常见的是 MySQL5.7 和 8.0 都在用8.0 是 LTS 版本8.4 也已经是 LTS新开实验我一般默认选 8.0 往上走因为窗口函数、CHECK 约束、公用表表达式这些特性在写进阶实验时都用得上。连接命令和建库命令是环境章里必须出现的两块。常见做法是先在命令行客户端里验证连通性mysql -h127.0.0.1 -P3306 -uroot -p这段命令尝试从本机连接到 3306 端口的 MySQL 服务-u指定用户-p表示后续输入密码。连不上时优先检查服务是否启动、端口是否被占用以及用户是否允许从当前主机登录。建库时直接指定字符集避免后续中文乱码CREATE DATABASE IF NOT EXISTS zzu_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;utf8mb4是完整的四字节 UTF-8 支持能存中文、表情符号和生僻字0900_ai_ci是 MySQL 8.0 引入的排序规则基于 Unicode 9.0比较规则更符合日常直觉。如果实验环境是 MySQL 5.70900_ai_ci不可用要退回来用utf8mb4_general_ci。这个细节我会写进报告书的「环境说明」里属于评审老师一眼能看到的加分项。如果课程组要求演示国产数据库常见的选择是人大金仓 KingbaseES 或达梦 DM。它们的 SQL 大体兼容 MySQL/PostgreSQL 体系但要注意三点一是自增列写法不同达梦兼容 Oracle 模式倾向使用序列金仓在 PostgreSQL 兼容模式下直接支持SERIAL二是分页查询的写法有差异三是系统函数命名不完全一致比如日期函数、字符串拼接符号。报告书里遇到这种差异我会单独列一张「兼容性对照表」把本实验用到的 SQL 在两种库里的写法各给一列。3.2 建表时把约束写全主键、外键、唯一键、CHECK 与字段注释建表语句是实验报告书里最容易暴露水平的部分。只写字段名和类型是不够的主键策略、外键行为、唯一约束、默认值、字段注释这些都要有。下面是一个学生基础表的例子可以直接抄进报告书的附录CREATE TABLE student ( sid CHAR(10) NOT NULL COMMENT 学号固定 10 位, sname VARCHAR(20) NOT NULL COMMENT 学生姓名, gender CHAR(1) NOT NULL DEFAULT M COMMENT 性别M/F, birthdate DATE NOT NULL COMMENT 出生日期用于年龄计算, email VARCHAR(50) NULL COMMENT 邮箱作为唯一性辅助凭证, phone VARCHAR(20) NULL COMMENT 联系方式, PRIMARY KEY (sid), UNIQUE KEY uk_student_email (email), CONSTRAINT chk_student_gender CHECK (gender IN (M,F)) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表;这里有几个参数值得在报告书里展开。学号用CHAR(10)而不是VARCHAR(10)因为定长学号用定长类型能省去变长字段的长度计算开销UNIQUE KEY给 email 做了唯一约束这是逻辑设计阶段「候选键」的落实CHECK约束在 MySQL 8.0 之前会被解析但忽略如果你用的还是 5.7行为是不报错但不生效这一点建议在报告里注明显得你不是照抄。ENGINEInnoDB明确指定事务型存储引擎后面演示事务回滚才有前提。建表顺序也很讲究。有外键的表要在被引用表之后创建否则会报 1215 错误无法添加外键约束。如果实验涉及多张表我习惯按「主表 → 从表 → 中间表」的顺序排列建表脚本报告书附录里的 SQL 文件也按这个顺序组织别人导入时才不会踩顺序坑。3.3 增删改查与事务一组能直接写进报告正文的 SQL 与预期输出数据库增删改查是报告书实验步骤里必须覆盖的重头戏。光放 SQL 不够每条要配一句「预期结果」。下面用一个简化版选课场景演示标准写法-- 插入课程验证 INSERT INSERT INTO course (cid, cname, credit) VALUES (C001, 数据库原理, 3); -- 查询课程验证 SELECT 与 WHERE SELECT cid, cname, credit FROM course WHERE credit 3; -- 更新学分验证 UPDATE注意带 WHERE UPDATE course SET credit 4 WHERE cid C001; -- 删除课程验证 DELETE DELETE FROM course WHERE cid C001;每条语句后面的注释直接点明它覆盖的实验点。UPDATE和DELETE必须带WHERE这是报告书里反复要强调的安全习惯——不带条件的更新会作用到整张表。真出现了全表数据被改的情况可以用事务回滚挽救这也是为什么下一段事务演示要和增删改查放一起。事务的经典演示代码是「先开启事务做一组操作再决定提交或回滚」START TRANSACTION; INSERT INTO course (cid, cname, credit) VALUES (C002, 操作系统, 4); UPDATE course SET credit 5 WHERE cid C002; ROLLBACK;这样执行完C002的插入和修改都会被撤销。报告书里我会紧接着展示一个反向例子把ROLLBACK换成COMMIT查询结果里能看到刚插入的行形成对照。这个对照是评审老师判断你有没有真正理解事务语义的关键点。事务部分还要提一下隔离级别和并发问题。常见做法是先用默认的REPEATABLE READ跑一遍实验再在报告书里讨论「如果不加锁两个事务同时改同一条数据会怎样」。数据库死锁在这里是个很好的案例素材两个事务各自先锁了对方要用的行然后互相等待最终有一个事务被回滚。演示死锁时把两张表的操作顺序故意写反就能稳定复现这部分放到后面的避坑章节细说。4. 常用实验选题的表结构模板学生选课与图书管理系统这两条路怎么走数据库课程设计的热门选题来来去去就那么几个学生选课系统和图书管理系统是出现频率最高的两条路。这类选题的共同点是实体关系清晰、功能扩展空间大适合从简单查询一路做到存储过程和并发控制。这一章给出可以直接套用的表结构设计字段、约束、类型参数全部可抄。4.1 学生选课系统学生、课程、选课三张表与多对多关系的标准实现学生和课程之间是多对多联系需要用中间表拆开。完整的关系模式是三张表student、course、sc选课表。字段设计如下表名字段设计约束与说明studentsid CHAR(10) 主键、sname VARCHAR(20)、gender CHAR(1)、birthdate DATE、phone VARCHAR(20)学号定长姓名非空coursecid CHAR(6) 主键、cname VARCHAR(30)、credit DECIMAL(2,1)、teacher VARCHAR(20)学分用 DECIMAL 而不是 FLOAT精确比较scsid CHAR(10)、cid CHAR(6)、score DECIMAL(5,2)、semester VARCHAR(10)复合主键 (sid, cid)两个外键级联删除复合主键 (sid, cid) 是这一步的关键设计。它既保证了同一学生对同一课程只能选一次也天然约束了中间表的粒度。外键的级联策略是另一个要写清楚的地方CREATE TABLE sc ( sid CHAR(10) NOT NULL, cid CHAR(6) NOT NULL, score DECIMAL(5,2) NULL, semester VARCHAR(10) NOT NULL, PRIMARY KEY (sid, cid), CONSTRAINT fk_sc_student FOREIGN KEY (sid) REFERENCES student(sid) ON DELETE CASCADE, CONSTRAINT fk_sc_course FOREIGN KEY (cid) REFERENCES course(cid) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT选课表;ON DELETE CASCADE的含义是删除学生或课程记录时选课表里相关的选课记录自动删除。这在实验场景里省了很多手工清理的活但报告书里要单独解释一句「为什么不用 RESTRICT」。简单说课程被删除后保留选课记录会让统计成绩时产生悬空引用级联删除更符合教务系统的业务直觉。成绩字段score DECIMAL(5,2)用定点数是因为浮点数在比较和求和时可能产生 0.1 0.2 不等于 0.3 的误差课程成绩不允许这种模糊。选课系统最常见的验证查询是「查某个学生的所有课程和成绩」用两表 JOIN 就能完成SELECT s.sname, c.cname, sc.score FROM student s JOIN sc ON s.sid sc.sid JOIN course c ON c.cid sc.cid WHERE s.sid 2024001;这段 JOIN 是报告书里必放的查询。说明部分我会写清楚连接顺序先取学生表找准学生再通过选课表找到课程编号最后关联课程表拿到课程名。三个表逐层关联结果集粒度等于选课记录条数不会因为 JOIN 产生重复行。4.2 图书管理系统馆藏、读者、借阅三张表与逾期还书判断图书管理系统同样三张表book、reader、borrow。与选课系统不同的是借阅关系带有明确的开始和结束时间逾期判断成为核心业务逻辑。字段设计如下表名字段设计约束与说明bookbid CHAR(8) 主键、title VARCHAR(50)、author VARCHAR(30)、publisher VARCHAR(40)、status TINYINTstatus 用 TINYINT0 在馆 1 借出readerrid CHAR(8) 主键、rname VARCHAR(20)、type VARCHAR(10)、max_borrow TINYINTmax_borrow 记录可借数量上限borrowborrow_id INT AUTO_INCREMENT 主键、bid CHAR(8)、rid CHAR(8)、borrow_date DATE、due_date DATE、return_date DATE NULL复合唯一键 (bid, rid, due_date) 防止重复借阅状态位status字段是图书管理系统的经典设计取舍。有人会只用borrow表里return_date IS NULL来判断书是否借出我建议保留冗余的status因为「在馆/借出」是高频查询加一个字段可以避免每次都要扫借阅表做子查询。报告书里可以主动讨论这个冗余设计「以查询性能换取更新一致性」属于逻辑设计阶段有思考深度的加分内容。图书管理里容易被测的查询是逾期统计。MySQL 里用DATEDIFF直接计算日期差SELECT r.rname, b.title, DATEDIFF(CURDATE(), br.due_date) AS overdue_days FROM borrow br JOIN reader r ON r.rid br.rid JOIN book b ON b.bid br.bid WHERE br.return_date IS NULL AND br.due_date CURDATE();这里的CURDATE()与服务端日期相关实验报告里要注明「以数据库服务器时间为准」如果服务器时区配置不正确CURDATE()结果可能比本地时间差 8 小时就会查出错误的逾期天数。关于时区的坑后面避坑章节单独展开。还书操作要包在事务里同时更新两处数据这也是报告书里演示事务原子性的好素材START TRANSACTION; UPDATE borrow SET return_date CURDATE() WHERE bid B2024001 AND return_date IS NULL; UPDATE book SET status 0 WHERE bid B2024001; COMMIT;两段 UPDATE 必须同时成功或同时失败。如果只更新了归还日期但忘了把书改回「在馆」后续这本书就会被重复借出这类 bug 是实验报告里最容易让老师眼前一亮的地方。我会在报告书里注明「如果第二步失败第一步也应回滚」然后贴出把COMMIT换成ROLLBACK后的查询结果作对比。5. 实验报告书里的五个高频坑从字符集乱码到数据库死锁的排查清单这一章写的是我在整理数据库实验报告过程中反复遇到的坑。每一条都按「现象 → 原因 → 解决」记录可以直接对照排查。5.1 导入脚本时外键约束失败报 1452现象用SOURCE或 Navicat 运行整个 sql 文件报Cannot add or update a child row: a foreign key constraint fails。原因脚本里建表顺序和外键依赖关系不匹配——向子表插入数据时主表对应记录还没插入。常见于从设计文档直接拷贝多张表数据没有按依赖顺序排列。解决给导出脚本统一加上外键检查开关或严格按主表→从表顺序插入。推荐做法是在脚本开头加这两行SET FOREIGN_KEY_CHECKS 0; -- 导入完成后 SET FOREIGN_KEY_CHECKS 1;注意这不是万能的它会跳过外键校验导入完成后要重新校验一遍数据完整性。报告书里写这个操作时一定补一句「生产环境不建议长期关闭外键检查」。5.2 中文存进去变问号字段显示乱码现象黑框命令行输入中文 INSERT 后查询结果是?或者 Navicat 里看起来正常命令行里读出来乱码。原因连接层字符集不一致。建库用 utf8mb4但命令行连接的character_set_client和character_set_results还是默认的 latin1中文在传输过程中被转码丢弃。解决连接时显式指定字符集mysql -uroot -p --default-character-setutf8mb4或者连上后在会话里执行SET NAMES utf8mb4;报告书的运行环境说明里要写上这一步。排查时用SHOW VARIABLES LIKE character_set%;一条命令就能看到哪些环节字符集没对齐把它们统一成 utf8mb4 即可。5.3 MySQL 8 下 GROUP BY 报错提示 ONLY_FULL_GROUP_BY现象在 MySQL 5.7 上能跑的统计查询到 8.0 报Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column。原因MySQL 8.0 默认sql_mode包含ONLY_FULL_GROUP_BY要求 SELECT 的非聚合列必须出现在 GROUP BY 中。很多人用 5.7 时习惯了宽松模式换到 8.0 就翻车。解决不用关 sql_mode把查询改成标准写法。比如统计每个学生的选课数SELECT s.sid, s.sname, COUNT(sc.cid) AS course_count FROM student s LEFT JOIN sc ON s.sid sc.sid GROUP BY s.sid, s.sname;注意s.sname必须出现在 GROUP BY 里。如果不想让姓名参与分组可以用子查询先取学号再关联SELECT s.sid, s.sname, t.course_count FROM student s LEFT JOIN ( SELECT sid, COUNT(cid) AS course_count FROM sc GROUP BY sid ) t ON s.sid t.sid;报告书里我会推荐第二种写法因为它的语义更清晰先算出每个学生的统计值再回表取姓名。临时关闭ONLY_FULL_GROUP_BY虽然能跑但写进报告书显得不够严谨我不建议。5.4 两个事务互相等锁报死锁后一个事务被回滚现象并发执行两个事务其中一个报Deadlock found when trying to get lock; try restarting transaction。原因两个事务按不同顺序获取行锁。比如事务 A 先锁 student 表的某行再锁 sc 表事务 B 先锁 sc 表再锁 student 表的同一组数据两者互相等待对方释放形成死锁。解决规范事务内操作顺序所有事务都按同一顺序访问多张表。比如约定「先锁主表再锁从表」两段代码都按这个顺序执行死锁就不会出现。排查死锁现场用这条命令SHOW ENGINE INNODB STATUS;输出里找到LATEST DETECTED DEADLOCK段落里面会列出两个事务各自持有的锁和等待的锁。报告书里写死锁演示时把这段输出截图放进去然后分析两行事务的锁顺序比贴理论定义有说服力得多。5.5 日期时间字段查出来和本地时间差 8 小时现象插入一条带时间的记录查询发现时间比本地时间晚或早 8 小时逾期还书统计也因此算错天数。原因数据库服务器的time_zone和客户端连接不在同一时区。MySQL 8.0 默认时区在某些环境里是系统 UTC而本地在东八区CURDATE()、NOW()都取服务器时间自然对不上。解决在连接串里显式指定时区或在建库后设置会话时区mysql -uroot -p --default-character-setutf8mb4SET time_zone 08:00;更彻底的做法是把全局时区改为东八区SET GLOBAL time_zone 08:00;报告书里我会在「运行与验证」章节注明每条带日期的查询是在什么时区下执行的。时区这类问题看起来像玄学实际上在日志输出、逾期计算、报表统计里都会露出马脚实验阶段就统一时区能省不少事。6. 让报告书在答辩时撑得住场面用一个批量执行脚本把整份报告过一遍报告书写完不是终点交付前我一定把附录里的 SQL 从头到尾跑一遍确保别人拿到手也能复现。这个过程用一个批量执行脚本就能完成既生成可复查的日志又能一次发现脚本里的顺序问题。#!/bin/bash # 按顺序执行 schema.sql 和 data.sql并把所有输出追加到 run.log for f in schema.sql data.sql queries.sql; do echo $f run.log mysql -uroot -p --default-character-setutf8mb4 zzu_db $f run.log 21 if [ $? -ne 0 ]; then echo FAILED: $f run.log fi done echo ALL DONE run.log这个脚本的逻辑很简单依次执行三个 sql 文件标准输出和错误输出都追加到 run.log。$?判断上一条命令的退出码非零就标记失败。参数说明里值得注意的只有--default-character-setutf8mb4和21——前者避免字符集乱码影响日志判断后者把错误信息也收进日志排查时不用回终端翻历史记录。写答辩用的报告我最后会做出三件套一份 PDF 报告、一个 sql 附件包、一段两分钟演示路径说明。演示路径说明要有「先干什么、预期看到什么、如果没看到说明什么」三段式描述比如「先查询选课统计预期每个学生一行记录如果行数多于学生数说明 JOIN 条件缺失导致笛卡尔积」。这种表述方式会让老师觉得你不只是跑通代码而是理解了每一个输出。以前我也试过写完报告不重跑结果答辩现场演示时报错只能解释「这是环境问题」。从那以后每次交实验前我都会把数据表清空重来一遍跑完脚本再检查 run.log 里的每一条查询输出。这个过程虽然机械但正是它把报告书从「写出来的材料」变成了「可复现的实验记录」。希望帮到你。本文还有配套的精品资源点击获取