简介:南华大学《数据库原理》课程实验报告完整版,面向大数据技术等专业学生,覆盖认识DBMS、简单SQL查询、高级SQL查询与数据更新四大实验模块。压缩包为1个doc文档,大小5.38MB,内容详细记录了两类创建数据库的方式:通过SQL Server Management Studio图形化操作和直接编写CREATE语句,并定义学生、课程、选修三张表的字段类型、主码外码、默认值及表间关系。实验报告整理了十余个单表查询和多表JOIN查询实例,如按姓名、成绩、出生日期筛选,分组统计选课数,查询选修某课程的计算机系学生以及间接先行课等,覆盖WHERE、ORDER BY、GROUP BY、JOIN等核心SQL子句。文档还涉及数据完整性与增删改操作,有助于理解数据库设计原则。该资源已有375人学习,适合课程复习、实验参考及SQL查询能力强化。
1. 数据库原理实验报告:为什么它比考试成绩更能暴露你的数据库水平
实验报告不是代码截图合集,数据库原理课的老师在评分时,最先找的并不是某条 SQL 是否跑通,而是你有没有把「实验目的—操作步骤—过程数据—结果分析」这条证据链写全。我见过不少同学 SQL 全部执行成功,却因为报告里只有建表语句和截图,分数反而不如一个运行结果有瑕疵、但分析写得扎实的报告。
数据库原理实验报告真正的考察点,是你能不能把课堂上学到的关系模型、完整性约束、范式、事务隔离这些概念,落在一个真实运行的数据库上,并且用文字和数字证明你理解了它们。这比期末试卷上的一道画 ER 图的题更能暴露真实水平,因为你没法背答案。
这篇笔记适合正在写数据库原理实验报告的人。不管你的实验题目是学生选课、图书管理还是订单库存,只要实验覆盖建库建表、SQL 查询、ER 设计、范式分解、事务或索引,这篇就能帮你把报告写成能复现、能对答的工程文档,而不是交差用的作业。
2. 动手前先想明白:实验报告的评分逻辑与标准骨架
2.1 老师在第一遍翻报告时,重点在看这三样东西
先别急着写代码,先看看评分的人在看什么。课程老师收到一批报告后,第一遍通常不是逐字读,而是翻。翻的时候关注三件事。
第一,证据链是否完整。从「实验目的」出发,能不能顺理成章地走到「实验结论」,中间每一个操作有没有留下输出数据。第二,是否可复现。环境版本、数据库名、表名、测试数据,这些信息缺了,别人按你的步骤根本无法重新跑一遍,报告的可信度就打折。第三,是否体现了理论。同样是建一张表,有人写完 CREATE TABLE 就结束,有人会接着写「为什么这里要用外键,为什么选 InnoDB 引擎,为什么字符集用 utf8mb4」,后者给老师的信号是完全不同的。
一份合格的数据库原理实验报告,本质是「用数据库操作说话」的论说文。每一个实验结论都得有对应的操作和输出作为论据,而不是只丢出结论。
2.2 一份不踩雷的实验报告骨架
一份标准的数据库原理实验报告,通常包含 8 个部分。下面这张表是我在写报告和帮人改报告时常用来对照的清单,每个部分写什么、常见坑在哪,表里说得很清楚。
| 报告章节 | 要写的内容 | 最常见的坑 |
|---|---|---|
| 实验目的 | 这个实验验证了什么原理、训练了什么能力 | 从课件上抄一句话,和后面操作对不上 |
| 实验环境 | 数据库版本、操作系统、客户端工具、字符集 | 只写数据库名,没写版本,结果无法复现 |
| 实验步骤 | 按编号写操作顺序,包含命令和关键输出 | 只写命令,没有中间输出 |
| 实验代码 | 带注释的 SQL 或脚本,核心语句要有说明 | 大段代码不注释,明显是粘贴的 |
| 实验结果 | 截图 + 关键行数、结果集摘要、耗时 | 截图没标注条件,或者只有图没有任何数字 |
| 结果分析 | 比较预期与实际,用理论解释差异 | 只写「运行成功」,没有对量的比较 |
| 问题与解决 | 写 1-2 个真正卡住你的问题及排查过程 | 编造问题,或写「没遇到问题」 |
| 实验结论 | 两三句话说明验证了什么原理 | 又把实验步骤复述了一遍 |
这 8 个部分不是摆设。写报告的时候,每写完一个部分,问自己一句:如果我是第一次看到这份报告,我能照着它把实验重新做一遍吗?如果答案是否定的,说明这个部分还没写到位。
2.3 实验记录:写报告前最重要的素材采集
很多同学写报告慢,是因为做实验的时候什么都没记,最后对着截图回忆,很多细节早就记不清了。我一般会在做实验的同时开一个 Markdown 文件,边做边记,格式非常简单:操作、命令、预期结果、实际结果、差异。
比如在做数据库完整性约束实验时,我插入一条违反外键的记录,预期是数据库报错,实际也确实报错了。我在记录里写:insert 一条 sc 表记录,sno='20240999'(该 sno 在 student 表不存在),预期报错误 1452,实际报错 1452,符合预期。这样一条一条记录,等写报告的时候,「问题与解决」和「结果分析」这两个部分几乎是现成的,不需要再回忆。
这套记录方式我还原封不动地放在第 6 章,做成了一张可以直接用的迷你记录表,建议你先往后翻一眼,再回来做实验。
3. 把五个高频实验写进报告:从原理说明到输出标注
3.1 建库建表与完整性约束:在报告里给每个约束找一个存在理由
建库建表是数据库原理实验的第一个模块,也是后面所有实验的地基。很多人的报告在这个部分只有几段 CREATE TABLE,没有说明,这很浪费。
先说建库到建表这一段代码,这是我在学生选课系统实验里用的最小版本,包含了主键、外键、唯一约束、检查约束和非空约束。
-- 建库:指定字符集,避免中文乱码 CREATE DATABASE IF NOT EXISTS student_course DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_course; -- 学生表:学号为主键,性别加检查约束 CREATE TABLE student ( sno CHAR(10) PRIMARY KEY, sname VARCHAR(20) NOT NULL, ssex CHAR(2) CHECK (ssex IN ('男','女')), sage TINYINT, sdept VARCHAR(30) ) ENGINE=InnoDB; -- 课程表:课程名唯一,学分必须大于 0 CREATE TABLE course ( cno CHAR(6) PRIMARY KEY, cname VARCHAR(30) NOT NULL UNIQUE, credit DECIMAL(3,1) CHECK (credit > 0) ) ENGINE=InnoDB; -- 选课表:学生和课程是多对多,用中间表表达 CREATE TABLE sc ( sno CHAR(10), cno CHAR(6), grade DECIMAL(5,2), PRIMARY KEY (sno, cno), FOREIGN KEY (sno) REFERENCES student(sno) ON DELETE CASCADE, FOREIGN KEY (cno) REFERENCES course(cno) ON DELETE RESTRICT, CHECK (grade IS NULL OR (grade >= 0 AND grade <= 100)) ) ENGINE=InnoDB;这段代码的每个约束都不是随便加的。sno 用 CHAR(10) 是因为学号是定长编码,专业课的老师看到你选 CHAR 而不是 VARCHAR,会认为你考虑过存储开销;student 表的主键是自然键,不是代理键,这符合课程上讲的实体完整性;sc 表用联合主键 (sno, cno),能直接防止同一学生选同一门课出现两条记录;外键的 ON DELETE CASCADE 和 ON DELETE RESTRICT 是有意区分给学生删除选课记录和删除课程这两种场景用的。
报告里除了放这段代码,我还建议补一条SHOW CREATE TABLE sc;的输出,让老师看到服务端实际解析出来的约束定义,而不是你写在代码里的原文。输出里如果有系统自动生成的约束名,说明你的约束真正落到了数据字典里,这一步能直接体现操作的完整性。
3.2 ER 模型到关系模式的转换:画图之外,规则要逐条落在报告里
ER 图实验的目的是让你掌握从现实世界抽象出实体、属性和联系的能力。很多报告画了一张漂亮的 ER 图,然后直接跳到了建表语句,中间缺了「转换为关系模式」这一步,而这恰恰是评分的关键。
ER 图转关系模式不是靠感觉,靠的是几条标准规则。我把这条规则表放到报告里,再结合自己的表逐条对照,会比只贴一张图清楚得多。
| ER 元素 | 转换为关系模式时的做法 |
|---|---|
| 实体 | 每个实体转成一张表,实体的属性转成表的字段 |
| 多对多联系 | 新建一张中间表,主键是两端实体主键的组合 |
| 一对多联系 | 在“多”端表中增加“一”端表的主键作为外键 |
| 一对一联系 | 任选一端放外键,并对方的表加唯一约束 |
| 派生属性 | 通常不单独建字段,查询时用表达式计算 |
拿学生选课系统来说,学生和课程之间存在「选修」联系,且是多对多。按照规则,学生实体转成 student 表,课程实体转成 course 表,多对多联系转成 sc 表,sc 表的主键是 (sno, cno),并且 sno、cno 分别作为外键引用两个实体表。
报告里这部分我会按照「ER 图 → 转换规则 → 关系模式列表 → 与建表语句对照」的顺序写。关系模式列表写成这样:学生(学号, 姓名, 性别, 年龄, 系别),课程(课程号, 课程名, 学分),选课(学号, 课程号, 成绩)。然后在下面写一句:「上述模式与第 3 章 / 第某节的建表语句一一对应」,让老师看到你不是画了一张孤立的图。
3.3 SQL 查询实验:让老师从语句和结果里看到你的查询设计能力
SQL 查询实验通常是报告里篇幅最大的部分。老师想看的不是你「会写」一条 SQL,而是你能说清楚「为什么这么写」。因此,每条查询我建议都按四段来组织:查询需求、SQL 语句、执行结果摘要、写法说明。
下面这个例子是把多表连接、分组、聚合、Having 和排序放一起的一条综合查询,很常见。
-- 需求:查询每个学生所选课程的门数和平均分,只统计选课数不少于 2 门的学生, -- 按平均分降序排列 SELECT s.sno, s.sname, COUNT(sc.cno) AS course_cnt, ROUND(AVG(sc.grade), 2) AS avg_grade FROM student s JOIN sc ON s.sno = sc.sno GROUP BY s.sno, s.sname HAVING course_cnt >= 2 ORDER BY avg_grade DESC;这条 SQL 的写法说明我一般会写三句话。第一,连接用显式 JOIN 而不是 FROM student s, sc WHERE s.sno = sc.sno,因为显式连接把连接条件和过滤条件分开,可读性更好。第二,GROUP BY 后面把 sname 一起带上,因为在 MySQL 8.0 的 ONLY_FULL_GROUP_BY 模式下,查询列里出现的非聚合字段必须出现在 GROUP BY 中。第三,HAVING 与 WHERE 的本质区别是,HAVING 是在分组后过滤,这里不能把 course_cnt >= 2 换到 WHERE 里。
再补一个稍微进阶一点的例子,关联子查询,用来查「比同系平均年龄大的学生」。
-- 需求:查询比同一个系的学生平均年龄更大的学生 SELECT sname, sage, sdept FROM student s WHERE sage > ( SELECT AVG(sage) FROM student WHERE sdept = s.sdept );这个例子的写法说明要点是:子查询里用s.sdept = sdept把内外层关联起来,所以它是关联子查询;它的执行次数和外部表的行数有关,数据量大时性能不一定好,换成分组连接通常更高效。在报告里写出这种性能意识,会让老师觉得你不是在背答案。
3.4 范式分解实验:用一组函数依赖把 1NF、2NF、3NF 走通
范式分解是最容易写废的一个实验,因为它很容易变成「我觉得这个是 2NF,我觉得那个是 3NF」。正确的姿势是先列出函数依赖,再按定义逐步判断和拆分。
我以一张典型的坏表为例:选课(sno, sname, sdept, cno, cname, credit, grade),意思是一个选课事实里塞进了学生信息和课程信息。这张表里存在这样几组函数依赖。
- sno → sname
- sno → sdept
- cno → cname
- cno → credit
- (sno, cno) → grade
先判断第一范式。这张表的每个属性都是不可再分的原子值,所以它已经是 1NF,但存在问题:sname、sdept 依赖 sno,而 sno 只是候选键 (sno, cno) 的一部分,这就是部分依赖。部分依赖会导致插入异常:一个还没选课的学生,因为缺 cno,学号和姓名插不进表里。
消除部分依赖,得到第二范式。把表拆成三张:
- student(sno, sname, sdept)
- course(cno, cname, credit)
- sc(sno, cno, grade)
接下来判断第三范式。第三范式要求消除非主属性对候选键的传递依赖,这三张表里都没有传递依赖,所以它们已经是 3NF。如果 student 表里再加一个系主任字段,变成 student(sno, sname, sdept, dept_head),那么存在 sno → sdept → dept_head 的传递依赖,就得继续拆。
报告里除了写分解结果,我还会做一步验证:把分解后的三张表做一次自然连接,看结果能不能和原始表完全对上。这一步能证明分解是无损的。验证 SQL 可以这样写。
-- 无损连接验证:连接后的行数和关键字段应与分解前的原表一致 SELECT s.sno, s.sname, s.sdept, c.cno, c.cname, c.credit, sc.grade FROM student s JOIN sc ON s.sno = sc.sno JOIN course c ON c.cno = sc.cno ORDER BY s.sno, c.cno;写报告时,把分解前后的表各放一份数据样例,再放这条验证 SQL 的结果行数,比任何文字描述都有说服力。
3.5 事务隔离级别与索引实验:现象比截图更重要
事务和索引实验通常是同一个实验放在最后,考察你对并发控制和查询优化的理解。这两个实验的特点都一样:光贴截图没有用,必须写清楚「在什么条件下观察到了什么现象」。
先看事务隔离级别实验,最常见的是用两个数据库会话来验证脏读。
-- 会话 A:设置为读未提交,观察其他会话能否读到未提交的数据 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; START TRANSACTION; UPDATE account SET balance = balance - 100 WHERE id = 1; -- 此时不 COMMIT,立刻切到会话 B 查询 -- 会话 B:同样设置为读未提交 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT balance FROM account WHERE id = 1; -- 如果读到了减 100 之后的余额,说明发生了脏读报告里,这段操作必须写成一步一步的对照:会话 A 做了什么,会话 B 在什么时间点查询,读到的值是多少,预期是多少。只要把时间顺序写清楚,老师就能理解你确实复现了脏读,而不是背结论。再把隔离级别改成 READ COMMITTED,重复同样操作,B 会话读到的应该是旧值,这个对照组是报告的加分项。
再看索引实验。很多同学做完发现加了索引和没加索引耗时差不多,就把结论写成「索引效果不明显」。这通常不是索引没用对,而是实验设计有问题。
-- 在 sc.grade 上建立二级索引 CREATE INDEX idx_sc_grade ON sc(grade); -- 用执行计划确认索引是否被使用 EXPLAIN SELECT * FROM sc WHERE grade > 85; -- 对比:删除索引后再看执行计划 DROP INDEX idx_sc_grade ON sc; EXPLAIN SELECT * FROM sc WHERE grade > 85;执行计划里重点看 type 列和 key 列。type 从 ALL 变成 ref 或 range,key 列出现 idx_sc_grade,才说明索引真正生效了。如果你的表只有几十行数据,优化器很可能选择全表扫描,此时应该在测试表里先插入几万行数据再测试。报告里把这两次执行计划的结果放在表中对比,并解释为什么数据量小时索引收益不明显,这个分析深度非常加分。
4. 避坑:数据库原理实验报告里最高频的五个翻车点
4.1 报告里只有代码和截图,没有过程数据
现象:一份报告交上来,从建库到查询全是 SQL 和结果截图,但每一段之间没有过渡,没有写为什么做这一步,也没有记录中间过程的输出。
原因:以为实验报告就是「代码 + 截图」的堆砌,没有意识到老师需要靠文字把代码串成逻辑线。
解决:在每个实验模块前加一两句「本步目的」,在关键输出后用一行写清「预期是什么,实际是什么」。截图不要截一整屏,把命令行里影响判断的报错信息、行数数字单独截出来并标注。
4.2 ER 图与建表语句不一致
现象:ER 图画的是多对多联系,但建表时没有建中间表;或者联系里的属性在建表语句里消失了。
原因:画图和建表是分开做的,画完 ER 图直接开始写 CREATE TABLE,跳过了关系模式转换这一步。
解决:写报告时把「ER 图 → 转换规则 → 关系模式列表 → 建表语句」按顺序全部呈现,每一步之间能相互对应。建完表后回看图,确认每个联系都有对应表或者外键。
4.3 范式判断只在结论里写「是 3NF」,没有过程
现象:结论直接写「符合 3NF」,但整篇看不到函数依赖的列表,也没有说明为什么不存在部分依赖或传递依赖。
原因:范式的判断成了「凭感觉」,没有把函数依赖拆解出来逐条对照。
解决:报告中统一采用固定流程:先列出所有函数依赖,然后标注候选键,判断有没有部分依赖,再判断有没有传递依赖,每一步各用一句话说明,最后给出分解结果。
4.4 事务实验复现不出脏读,就硬说现象不成立
现象:按教材操作,两个会话都执行了更新和查询,但始终读不到未提交的数据,于是报告里写「保持不变」,或者干脆虚构一个脏读结果。
原因:默认隔离级别是 REPEATABLE READ 或更高的级别,没有先把会话隔离级别改成 READ UNCOMMITTED;另外,操作顺序出了问题,B 会话在 A 事务提交之后才查询。
解决:写报告前先检查默认隔离级别,用SELECT @@transaction_isolation;确认,然后显式SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;。操作顺序要严格按「A 更新未提交 → B 查询 → A 回滚」来执行,并把每一步对应的时间记录在报告里。
4.5 索引实验效果不明显,直接下结论「索引没用」
现象:CREATE INDEX 之后,查询耗时没有明显变化,甚至 EXPLAIN 显示 type 还是 ALL,直接写「索引对查询没有优化作用」。
原因:表中数据量太少,优化器认为全表扫描代价更低;或者查询条件在索引列上做了函数运算,导致索引失效。
解决:先在表中插入至少几万行测试数据,再建索引并用 EXPLAIN 验证。报告中要同时记录优化器的 type、key 字段和实际耗时,并解释为什么数据量小时索引收益不明显,以及函数包裹索引列会导致索引失效。
5. 让报告经得起追问:结果分析、环境记录和答辩对答准备
5.1 结果分析的正确写法:先比较预期,再用理论解释差异
结果分析是报告里最能拉开差距的部分。一个不成熟的分析写法是:查询成功,结果包含 3 条记录,符合预期。这等于没分析。
更建议的写法是分两层。第一层是「量」的比较:预期多少行,实际多少行,为什么一致或不一致。第二层是「原理」解释:数据库的哪个机制造成了这个现象。比如在范式分解实验里,你验证了无损连接,分析时就应该写:分解后三表连接的行数与原始表相等,说明这次分解没有引入额外行,也没有丢失数据,符合无损连接定理中对候选键作为公共属性的要求。
如果实验结果和预期不一致,不要掩盖,这正是「问题与解决」部分的素材。我见过一份报告,做左连接时预期左表所有学生都在结果里,但实际少了两个,查到最后是那两名学生的 sc 表里有 grade 为 NULL 的记录,被某个条件过滤掉了。这个排查过程写出来,比顺利跑通十条 SQL 更有价值。
5.2 环境与样本数据:决定报告能否被复现的隐藏分数线
实验环境这栏,很多同学只写一句「Windows 11 + MySQL」,这不够。老师想复现你的结果时,缺一个版本号都可能让他跑不出同样的现象。
我一般会在实验环境里用一张表列全这些信息:
| 项目 | 需要写什么 | 为什么重要 |
|---|---|---|
| 数据库管理系统 | 产品名 + 具体版本,如 MySQL 8.0.35 | 不同版本的默认隔离级别、字符集行为不同 |
| 存储引擎 | InnoDB | 影响外键约束、事务与索引支持 |
| 操作系统 | Windows 10 / Ubuntu 22.04 | 影响大小写敏感、路径写法 |
| 客户端工具 | 命令行客户端 / 图形客户端及版本 | 影响编码显示和操作步骤 |
| 字符集 | utf8mb4 / utf8mb4_unicode_ci | 影响中文数据存储与比较结果 |
| 测试数据规模 | 表名 + 行数范围 | 影响索引实验是否有可观察效果 |
实验环境写得越具体,报告的可复现性就越强。不要觉得这是凑字数,这恰恰是工程文档和作业帖的区别。
5.3 在报告里埋三个「记忆点」,答辩时你就有的说了
如果课程要求答辩或者演示,报告里最好埋三个可以主动展开的点。第一个点是一个踩过的坑,比如「我把外键删了,重新建表时一直报错无法删除,最后发现是因为约束名冲突,先 DROP 了外键才成功」,这类细节很容易引起共鸣。第二个点是一次对照实验,比如「同一公司际在做索引实验时,故意用 WHERE 条件中的函数包裹列让索引失效,我发现 type 变成了 ALL」,这展示了你对查询优化器的理解。第三个点是一句对理论的质疑,比如「为什么这里用 CASCADE 而不是 SET NULL」,能引导老师和你讨论,而不是单方面审问。
埋记忆点的原则是真实。宁可写一个结果不对但排查思路清晰的实验,也不要编一个完美但空洞的实验。老师见过的报告太多,编的痕迹很容易看出来。
6. 让报告立刻涨一个档次的技巧:先填一张「迷你实验记录表」再写正文
6.1 记录表长什么样,以及它怎么帮你省时间
写报告最花时间的,是把实验过程和结果组织成有逻辑的文字。我在帮课程助教改报告时,发现效率高的同学都会做一种「迷你实验记录表」,格式非常简单。
| 实验编号 | 操作对象 | 操作内容 | 预期结果 | 实际结果 | 是否一致 | 原因分析 |
|---|---|---|---|---|---|---|
| 4.1 | sc 表 | 插入不存在 sno 的选课记录 | 报外键约束错误 1452 | 报错误 1452 | 是 | 外键约束生效 |
| 4.2 | account 表 | 两个会话演示脏读 | READ UNCOMMITTED 下读到未提交值 | 读到了 | 是 | 隔离级别未阻止未提交读 |
| 5.1 | sc 表 | 对 grade 建索引后 EXPLAIN | type 从 ALL 变为 range | type 为 range,key 为 idx_sc_grade | 是 | 索引被优化器选用 |
这张表的好处有三个。第一,做实验时顺手就填了,不需要事后回忆。第二,它把「预期和实际」的对比前置到实验阶段,哪一步出了问题当下就能发现。第三,写报告时,这张表的每一行可以直接扩展成一个结果分析段落,顺序都不用调整。
我现在的习惯是:每个实验做完先不急着截图,把这张表填完才动笔写正文。上次做索引实验,就是因为表里「实际结果」一栏的耗时和预期差得离谱,排查后发现是 WHERE 条件对索引列用了函数,索引根本没生效。这个差错最后直接写进了问题分析部分,报告反而比那些全程顺利的实验更耐看。希望帮到你。
本文还有配套的精品资源,点击获取