数据库实验报告书写作全攻略:环境配置、查询索引与事务验证 简介《zzu数据库实验报告书》是一份面向计算机科学与技术、软件工程专业学生的数据库系统原理课程实验合集围绕openGauss数据库环境依次覆盖认识DBMS、创建数据库表与索引、交互式SQL语句、创建视图、完整性与安全性控制、事务与并发控制、数据库备份与恢复以及使用JDBC连接数据库等九大实验模块可帮助学习者巩固课堂理论、建立从数据建模到实际操作的完整认知。资源包共1个文件为docx格式的实验报告正文整体大小3.23MB报告结构清晰、环节完整包含实验目的、操作过程、问题更正、结果分析与实验截图说明。目前已有1521人学习下载适合正在修读数据库原理课程、需要撰写实验报告或准备复习的在校生。对于希望掌握openGauss常用命令、SQL开发及数据库安全、并发处理等核心技能的学习者这份报告可提供直接的实践参照与排版范本。1. zzu数据库实验报告书到底交付什么先搞清楚评分逻辑再动手如果把某高校代号课程里的数据库实验报告书当成一次普通作业来赶很容易在提交前夜翻车。这门课的交付物并不是SQL代码而是那份报告书。老师主要通过它判断你有没有真的在数据库上做过实验能不能把一份代码讲清楚。很多同学花大量时间把SQL跑通报告书却只有截图加源代码结果分很低也有人代码写得一般但把设计过程、验证数据和踩坑记录写清楚了反而拿到了不错的分数。这篇笔记会把一份能拿出手的实验报告书拆开讲环境怎么交代、建库脚本怎么落盘、正文按什么结构组织、查询索引事务这几类必做实验怎么做成可复现结果以及最容易翻车的细节和提交前的自查方法。2. 报告书先拼环境数据库选型、连接参数与建库脚本约定2.1 为什么报告书第一页要写清环境选型与版本约定数据库实验和普通编程实验不一样结果高度依赖运行环境。同一个SQL语句在不同版本上跑出来的排序可能不同对窗口函数和公共表表达式的支持也完全不同。报告书第一页写清环境是为了让读报告的人能复现你的操作也是给自己留后路期末复查时能还原现场不用靠记忆填参数。如果第一页缺了版本信息老师想验证附录里的脚本时第一步就会卡住。选型方面常见做法是跟着课程指定的数据库系统走如果课程没有硬性规定我一般会用MySQL 8.x作为默认选项。原因是大多数同学对它最熟悉安装包小命令行工具齐全遇到问题容易搜到答案少数涉及对象关系特性的实验会切换到PostgreSQL系。不推荐在一门课中途换库因为导出导入格式有差异报告书前后会自相矛盾连接参数的写法也要全部重来。报告书第一页的环境信息表可以按下面这个模板写提交前逐项核对一次。项目版本/参数报告书里的写法数据库系统MySQL 8.xInnoDB 引擎写明主版本即可不用写补丁号客户端工具命令行 mysql 客户端或图形工具当前稳定版写工具名称不写破解版或绿色版字样操作系统Windows 11 / Ubuntu 22.04 等写本机实际系统字符集utf8mb4 / utf8mb4_general_ci建库参数里明确写出连接方式127.0.0.1:3306课程专用账号密码可以打码端口和主机名要保留2.2 最小建库脚本把DDL和初始化数据落盘到init.sql报告书里最容易被忽略的一步是只把表和 SQL 贴在文档里却不肯保留一份独立可执行的建库脚本。实际上指导老师复查时最常做的事就是拿到附录里的脚本跑一遍。如果脚本缺了依赖关系或者建表语句和正文截图不一致一下就露馅了。所以项目一开始就要建立 db 目录把建库脚本命名为 init.sql 保存下来报告书附录里附上同名文件内容。先创建目录和脚本文件mkdir -p db cd db touch init.sql然后在这个文件里写入建库建表和初始数据。下面是一个最小示例对应学生选课场景-- 数据库课程实验报告书的初始化脚本 init.sql -- 目标库course_lab字符集 utf8mb4 DROP DATABASE IF EXISTS course_lab; CREATE DATABASE course_lab DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE course_lab; -- 学生表学号为主键年龄允许 NULL 以演示约束 CREATE TABLE student ( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT NULL, class_name VARCHAR(50) DEFAULT 未分班 ) ENGINEInnoDB; -- 课程表课程号固定 8 位字符 CREATE TABLE course ( course_id CHAR(8) PRIMARY KEY, course_name VARCHAR(100) NOT NULL, credit DECIMAL(3,1) DEFAULT 1.0 ) ENGINEInnoDB; -- 选课表联合主键外键引用 student 和 course CREATE TABLE enrollment ( student_id INT NOT NULL, course_id CHAR(8) NOT NULL, score DECIMAL(5,2) NULL, PRIMARY KEY (student_id, course_id), CONSTRAINT fk_enroll_student FOREIGN KEY (student_id) REFERENCES student(id), CONSTRAINT fk_enroll_course FOREIGN KEY (course_id) REFERENCES course(course_id) ) ENGINEInnoDB; -- 先插入父表再插入子表避免外键校验失败 INSERT INTO student (id, name, age, class_name) VALUES (1, A同学, 20, 计科1班), (2, B同学, 19, 计科1班), (3, C同学, NULL, 计科2班); INSERT INTO course (course_id, course_name, credit) VALUES (CS101, 数据库原理, 3.0), (CS102, 数据结构, 3.5); INSERT INTO enrollment (student_id, course_id, score) VALUES (1, CS101, 88.5), (2, CS101, 76.0), (3, CS102, NULL);这段脚本有两个关键设计一是字符集统一写在 CREATE DATABASE 后面后续所有表都继承库级配置避免单表单独设置造成不一致二是插入顺序严格按照父表到子表先把 student 和 course 的数据准备好再插 enrollment这样外键约束不会中途报错。报告书正文里建议在附录前加一句说明标明测试通过的环境版本例如“该脚本在 MySQL 8.xutf8mb4 环境下验证通过”。2.3 连接参数的三个必调项字符集、时区、自动提交很多实验报告里的异常现象最后都排查到连接参数上。比如用图形工具连库时中文正常换成命令行执行就乱码比如事务实验里明明执行了提交另一个窗口却读不到修改。这些往往不是数据库本身的问题而是连接设置和库端设置不一致。写报告书之前先统一三件事连接字符集、时区、自动提交。下面的 Python 连接示例可以直接放到实验环境里验证import pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, passwordyour_password, databasecourse_lab, charsetutf8mb4, # 与建库字符集保持一致 autocommitFalse, # 关闭自动提交事务实验才能控制 init_commandSET time_zone 08:00 # 统一时区避免日期函数结果漂移 )这里的 charset 必须和 init.sql 里的库级字符集一致否则查询结果里的中文在 Python 侧会显示乱码。autocommit 设成 False 是为了让事务控制在应用层显式提交或回滚否则 UPDATE 语句一执行就生效后面再做 ROLLBACK 也救不回来事务实验就没法演示了。time_zone 这一项容易被忽略一旦系统默认时区不是东八区NOW() 和日期格式化函数的结果会差出好几个小时两个终端分别连接时更难以对齐现象。报告书里怎么处理这三项不需要贴一大段连接代码而是在每个涉及事务和日期函数的实验开头用一行文字说明“本实验连接参数为 utf8mb4、autocommitFalse、东八区”。这样老师复查时即使遇到怪现象也知道是环境差异造成的不是实验逻辑本身错了。3. 报告书正文结构按题目、设计、过程、验证四段组织3.1 四段式结构下每类实验的写法差异数据库实验报告书最常见的通病是把所有实验题的代码按顺序贴一遍每题只有题目、SQL、截图没有设计说明也没有验证过程。这种文档看起来完整实际上既无法证明结果是你跑出来的也没法让读者理解为什么这样写。比较好的做法是每道题统一按四段组织题目描述、关系设计与思路、执行过程、验证结果与分析。不同实验类型在这四段里的侧重点不太一样。实验类型重点写哪几段常见丢分点建库建表关系设计、约束说明只贴 DDL不解释主外键和约束查询实验执行过程、索引影响分析只给结果集不放执行计划事务实验过程、现象对比没有并发窗口的对比描述存储过程参数设计、调用过程不写调用测试和返回值以查询实验为例题目描述只占两三行核心要写清楚“为什么用 JOIN 而不是 WHERE 连接”以及“这条查询在数据量多大时跑得动”。事务实验则相反题目本身很简单重点是过程中两个终端各自看到了什么提交前和提交后另一个连接能否读到变化。这些内容不能靠模板生成必须对着自己实际跑出来的结果写。3.2 从实验题目到E-R图再到关系模式先画图再写SQL实验报告书里最难的一部分不是把 SQL 跑通而是把“题目描述”转化成 E-R 图再转化成关系模式最后才落到建表语句。很多同学跳过中间两步直接凭感觉写 CREATE TABLE结果一张表的主键和外键都定了却没有覆盖题意里的某个联系后面再改表结构成本很高。我一般会在报告书里放一个表格形式的实体属性清单不强制画图工具但一定要把实体、属性、主键写清楚。比如“学生选课”这个场景可以整理成下面这样实体/联系属性主键/外键学生学号、姓名、年龄、班级主键学号课程课程号、课程名、学分主键课程号选课联系学生号、课程号、成绩联合主键学生号课程号外键学生号、课程号这个表格写完之后关系模式自然就出来了。学生关系模式学生(学号, 姓名, 年龄, 班级)主键学号。课程关系模式课程(课程号, 课程名, 学分)主键课程号。选课关系模式选课(学号, 课程号, 成绩)主键为学号和课程号的组合同时学号引用学生表课程号引用课程表。把这三种表示方式按顺序放进报告书阅卷老师能一眼看出设计过程是完整的而不是从某个模板里复制来的。如果题目涉及多对多联系比如一个学生选多门课、一门课被多个学生选就必须加中间表。这时候把 E-R 图转关系模式的规则写上多对多联系单独成为一张表两端实体的主键进入联系表做外键联系本身的属性也放在联系表里。这样实验 1 的建库部分就不再是简单罗列 DDL而是有清晰推导过程的设计文档。3.3 验证数据这样构造边界数据比随机数据更说明问题报告书里“验证”这一段是最能拉开分差的地方。只贴 SELECT 结果集只能证明这条 SQL 能跑但设计一组边界数据证明某个约束在异常情况下也同样生效才说明你理解了这个约束的作用。我会在每张表里额外准备几条“故意让它失败”的数据用来验证 NOT NULL、主键冲突、外键悬空等规则。下面这组边界插入语句可以直接放到实验报告书的验证部分-- 验证 name 字段 NOT NULL 约束预期失败 INSERT INTO student (id, name, age, class_name) VALUES (4, NULL, 21, 计科1班); -- 验证主键唯一性预期失败因为学号 1 已存在 INSERT INTO student (id, name, age, class_name) VALUES (1, D同学, 20, 计科1班); -- 验证外键约束预期失败因为学号 99 在 student 表中不存在 INSERT INTO enrollment (student_id, course_id, score) VALUES (99, CS101, 60.0);第一条数据在字段约束层面触发失败第二条在主键索引层面触发失败第三条在外键校验层面触发失败。报告书里不能只写“运行报错”应该把每条语句对应的错误码或错误提示写出来比如“Duplicate entry 1 for key student.PRIMARY”然后补一句“该错误符合预期证明主键唯一性约束生效”。这种写法让验证部分变成了可复现的检查清单而不是单纯的截图合集。构造边界数据还有一个注意点不要只构造失败数据成功数据也要覆盖业务边界。比如选课成绩允许 NULL就要有一条成绩为 NULL 的选课记录用来验证后续统计 AVG 时 NULL 不会被计入。这一类数据正是查询实验里结果和预期之间最容易出现偏差的地方提前在实验 1 的初始化脚本里埋好后面实验会省很多事。4. 核心实验做扎实查询、索引、事务三类必做内容的可复现写法4.1 多表连接与分组聚合执行顺序决定SQL怎么写数据库课程实验里查询类实验通常绕不开多表连接、分组聚合和排序。很多新手在这里翻车不是因为语法记不住而是没搞清楚 SQL 各子句的执行顺序结果把聚合函数写进 WHERE或者对别名做过滤而报错。一条 SQL 的完整执行顺序是先取数据源再过滤数据源再分组再过滤分组结果接着排序最后投影。这个顺序直接决定了写法。以学生选课为例如果题目要求统计每门课程的选课人数和平均分同时只看学分不低于 2.0 的课程并且只需要有选课记录的课程标准写法如下SELECT c.course_name, COUNT(e.student_id) AS enroll_count, AVG(e.score) AS avg_score FROM course c LEFT JOIN enrollment e ON c.course_id e.course_id WHERE c.credit 2.0 GROUP BY c.course_id, c.course_name HAVING enroll_count 1 ORDER BY enroll_count DESC;这段代码里有几个容易写错的地方。WHERE 发生在分组之前所以它只能过滤 course 表的原始字段 credit不能写 enroll_countHAVING 发生在分组之后所以它可以引用 COUNT 的别名 enroll_countGROUP BY 后面不能只写 c.course_id必须把 c.course_name 也带上否则某些窗口模式下会因为 ONLY_FULL_GROUP_BY 直接报错。报告书里遇到这类查询我建议把执行顺序写进分析段比单纯贴结果集更能说明原理理解程度。如果老师进一步要求分析性能可以在查询前加 EXPLAIN把执行计划放进报告书。报告书里只需要贴出 type、key、rows 这三列并用自己的话解释这条查询走了哪个索引大致扫了多少行还有没有全表扫描风险。这样比贴一大张执行计划截图更清楚。4.2 索引实验的必调参数EXPLAIN、覆盖索引、复合索引字段顺序索引类实验在报告书里常常变成走过场。建一个索引跑一遍结果变快结束。但实际上索引实验要写清楚三个问题为什么建这个字段、怎么验证生效、复合索引的字段顺序有什么讲究。先看一个带索引的对比场景。课程实验里如果频繁按 student_id 查选课记录初始状态可能没有索引EXPLAIN 显示全表扫描建立一个复合索引之后扫描行数明显变少。-- 在选课表上建立复合索引 ALTER TABLE enrollment ADD INDEX idx_stu_course (student_id, course_id); -- 查看执行计划 EXPLAIN SELECT student_id, course_id, score FROM enrollment WHERE student_id 1;复合索引 idx_stu_course 的字段顺序是 student_id 在前course_id 在后。这个顺序的选择依据是最左前缀规则查询条件里只要包含 student_id索引就能命中如果查询只带 course_id这个索引就帮不上忙。报告书里要用实验数据说话做法是分别对“有索引”和“无索引”两种状态执行 EXPLAIN然后把 type 和 rows 放在对比表格里。状态typerows结论无索引ALL3全表扫描有 idx_stu_courseref1索引生效扫描行数下降还要注意一个细节不是所有查询都需要索引。如果表里只有三行数据全表扫描和索引查找的差异肉眼几乎看不出来反而因为索引维护增加了写入成本。报告书里如果写了“加索引后变快了”要确认数据量是否足够支撑这个结论否则会被阅卷老师质疑实验设计不严谨。覆盖索引也是一个可以提的点查询列恰好都在索引里时回表次数为 0可以通过 Extra 列里的 Using index 来验证。建议把这一项写进进阶分析分值会明显不一样。4.3 事务实验隔离级别、提交与回滚的验证脚本事务实验是报告书里最难用截图证明真实性的实验因为它的核心现象是“另一个连接看不到未提交的数据”。只贴一条事务代码然后说“成功了”等于没有做实验。正确做法是开两个终端一个终端执行更新但不提交另一个终端在同一个隔离级别下查询观察结果是否可见然后对比提交之后的结果。把这个过程完整描述出来才是真正可复现的事务验证。下面这段 Python 脚本可以用来模拟事务控制import pymysql # 连接A事务中执行更新但不提交 conn_a pymysql.connect(host127.0.0.1, port3306, userroot, passwordyour_password, databasecourse_lab, charsetutf8mb4, autocommitFalse) cur_a conn_a.cursor() cur_a.execute(SET TRANSACTION ISOLATION LEVEL REPEATABLE READ) cur_a.execute(UPDATE enrollment SET score 90 WHERE student_id 1 AND course_id CS101) # 在连接A内查询能看到自己更新的数据 cur_a.execute(SELECT score FROM enrollment WHERE student_id 1 AND course_id CS101) print(连接A事务内查询:, cur_a.fetchall()) # 连接B新建连接此时做同样的查询 conn_b pymysql.connect(host127.0.0.1, port3306, userroot, passwordyour_password, databasecourse_lab, charsetutf8mb4, autocommitFalse) cur_b conn_b.cursor() cur_b.execute(SELECT score FROM enrollment WHERE student_id 1 AND course_id CS101) print(连接B在A提交前查询:, cur_b.fetchall()) # 期望看到原来的分数 conn_a.rollback() # 回滚连接A的更新 conn_a.close() conn_b.close()这段代码的关键在于连接A执行了 UPDATE 但回滚连接B在同一个 REPEATABLE READ 隔离级别下查询结果应该保持原值。报告中要写清楚这个现象并解释回滚让更新失效了所以看不到修改。如果把 autocommit 设成 True连接A执行完 UPDATE 的瞬间事务就已经提交连接B立刻就能查到新值那么整个实验就失去对比意义了。这就是为什么前面强调连接参数里必须显式控制 autocommit。另一个值得写的点是隔离级别之间的差异。把 SET TRANSACTION ISOLATION LEVEL 改成 READ COMMITTED 或者 READ UNCOMMITTED再跑同一段代码观察到的结果会不一样。如果课程没有要求深挖可以不展开但至少要把当前用的隔离级别写进报告书否则老师无法判断你的现象描述是基于哪种隔离级别得出的。5. 避坑数据库实验报告书最常见的五个翻车点5.1 截图里的结果和提交的.sql跑出不一样先定稿再截图现象老师复查时手动执行附录里的 init.sql发现建表字段和正文截图对不上或者查询结果少一行直接判定报告书抄袭或敷衍。 原因写报告时先跑了旧版本代码后面又改了 SQL却忘了重新截图图里是旧结果附录里已经是新代码。 解决规定动作是“先定稿代码再生成报告”。所有实验代码先在 db 目录里跑通并确认最终版然后按最终版重新截图保证报告书里每一处结果都来源于当前提交的脚本。提交前一天再抽查一条查询对着附录代码手动执行一次如果结果不一致重截该处截图。5.2 中文乱码和字符集不统一根因在四层不一致现象报告书附录里的建库脚本在课堂上执行时中文全部变成问号结果集里的中文也是乱码。 原因建库时用的是 utf8mb4但 sql 文件本身被某个编辑器保存成了 GBK或者命令行客户端连接的默认字符集不是 utf8mb4。要么是库端不一致要么是客户端连接端不一致四层里任何一层出问题都会乱码。 解决统一用 utf8mb4 一条路走到底。建库参数写 utf8mb4连接参数写 charsetutf8mb4文件保存编码确认是 UTF-8命令行执行前执行一句 SET NAMES utf8mb4。报告书里可以在环境表加一行“字符集规范”写明统一策略这样即使真的乱码也容易排查。5.3 外键插入顺序引发的假报错从父表到子表现象初始化数据执行到一半插入 enrollment 表时报外键约束错误学生以为脚本写错了。 原因不是 SQL 拼写错而是插入顺序违反外键依赖。先插了子表但父表里还没有对应记录。 解决所有 INSERT 按父表到子表的顺序执行。init.sql 里先插 student 和 course再插 enrollment。如果实验数据里确实需要先处理子表可以临时把外键检查关掉执行完数据插入后再恢复命令是 SET FOREIGN_KEY_CHECKS0 和 SET FOREIGN_KEY_CHECKS1。报告书里出现这类操作时要把开关语句写进脚本不能只在心里恢复。5.4 多题结果混在一起题号注释加结果标题现象报告书正文连续贴了多条 SQL 和多个结果集但看不出来哪条对应哪个实验题。 原因代码直接复制没有添加题目标注结果集也没有命名读者只能靠猜。 解决每条 SQL 前用注释写清题号比如 -- 实验2.1查询学分大于3的课程结果集前加一行加粗标题“实验2.1 执行结果”。这样整份报告书从第一章到最后一章段落之间边界清晰老师复查时对着题号就能快速定位。另外一个细节是多条查询共用一张结果图时容易漏行建议每条查询单独截结果集不拼接。5.5 换机器导入报Unknown database先连库再source现象把 init.sql 拷贝到另一台机器上执行报错 ERROR 1049: Unknown database course_lab但脚本开头明明写了 CREATE DATABASE。 原因客户端工具或命令行在执行脚本前就已经指定连接到一个不存在的数据库然后脚本中的 USE course_lab 找不到目标库。 解决在真正的命令行环境里先不带库名登录登录后再执行 source 命令或者在执行时使用 mysql -u root -p init.sql而不是 mysql -u root -p course_lab init.sql。如果在图形工具里执行也要先确认连接对象里没有指定具体的 database。报告书附录里如果包含建库脚本建议在说明文字中加一句“执行时先登录再 source”这个提示能避免复查时被环境问题卡住。6. 报告书收尾用评分点自查一遍再提交6.1 五分钟自查清单照着这份表过一遍提交前最有效的动作是把自己想象成阅卷老师手里只有这份报告书从头到尾读一遍看能不能完全复现。我会默认每份报告书都会被抽查执行所以自查清单里每一项都很具体。检查点快速检查方法合格标准环境信息完整翻到第一页版本、字符集、端口、账号说明齐全附录与正文一致随机挑一条SQL手动执行结果和截图一致题号与代码对应搜索“实验X.Y”每题都有对应段落中文无乱码打开附录检查中文所有注释和数据可读约束有验证找边界数据段落有预期结果和实际结果事务现象有对比看事务实验是否写了两个连接有提交前/提交后对比这些检查点做下来不会超过五分钟但能拦住八成以上的低级失误。尤其是“随机挑一条SQL执行”这一条截图和代码不一致的问题只有真正跑一遍才能暴露。6.2 把“失败记录”写成差异化素材避免千篇一律报告书里真正体现个人痕迹的不是大段正确的代码而是你遇到的问题和解决的思路。同一个实验每个同学踩的坑都不一样有人卡在外键顺序有人卡在乱码有人卡在事务隔离级别。把这些失败记录写进报告书末尾的“问题与解决”段落反而是很自然的差异化素材。我会在每份报告书最后保留一个小节专门记录两三条实验过程中的具体问题。内容不用长但要有真实的报错信息和调整过程。例如“第一次插入时报外键错误检查发现子表数据先于父表插入调整顺序后通过”比任何模板都更有说服力也能让阅卷人确信实验是你亲手做的。写报告书不需要追求完美无错反而要把试错痕迹保留下来这才是做实验的本来面目。提交前我也会从指导老师视角从头读一遍想象自己只有这份报告书能不能把实验完整复现出来。如果中途需要猜环境、猜参数就说明缺了说明。把这个习惯坚持几轮之后报告书的质量会稳定下来。希望帮到你。本文还有配套的精品资源点击获取