首页 / 资讯中心 / 文章详情

数据库概论实验报告:从E-R图到事务隔离级别的完整实践与踩坑指南

数据库概论实验报告:从E-R图到事务隔离级别的完整实践与踩坑指南 ★ FEATURED ARTICLE
简介这是太原理工大学软件工程专业数据库系统原理课程的实验报告围绕交互式SQL语句使用、数据完整性、用户鉴别与数据控制三个实验展开。报告以SQL Server 2016为平台从创建、修改、删除基本表开始逐步完成索引建立、视图创建、数据插入与更新并附有完整的SQL命令和运行结果内容既覆盖了表结构设计、主外键约束、唯一与聚簇索引等基础操作也体现了权限控制与数据安全要点。压缩包共1个文件为docx文档大小约1.69MB排版清晰便于直接阅读和对照练习目前已有311人学习下载。这份报告既适合正在学习数据库概论的学生复习SQL语法、完成实验作业也可作为撰写数据库实验报告的参考模板帮助读者快速掌握数据库对象管理与数据维护的完整流程。1. 一份数据库实验报告为什么值得当成项目来做数据库概论实验报告在很多同学眼里就是“老师布置的作业”交上去打分、归档、完事。但如果你把它当作一个完整的项目来拆解——需求分析、概念设计、逻辑设计、物理实现、数据填充、查询验证、异常排查——你会发现这份报告几乎是数据库开发全流程的微缩沙盘。我见过不少工作两三年的开发者回过头来被问“你数据库的隔离级别到底解决什么问题”答得磕磕绊绊倒是当年实验报告里一笔一画写的并发问题让他们捡回了记忆。这篇笔记就用某高校软件工程专业的“数据库概论实验报告”为样例把从建库到写报告的整套流程、参数设置和踩坑点拆开讲。适合正在做课程实验的同学也适合带实验的助教拿来当排查清单。2. 实验环境与数据库设计先把地基打对后面才有得写2.1 选型为什么课程实验默认用 MySQL 8.x 而不是 5.7课程实验最常见的数据库选型就是 MySQL原因很简单安装快、文档多、语法跟教材里的标准 SQL 偏差小。但版本选择值得说一句。MySQL 5.7 在很多实验室机器上还是默认版本可如果你在实验报告里写“使用 MySQL 5.7”那么窗口函数ROW_NUMBER、RANK 这类就没法演示因为 5.7 不支持。而实验题目里只要出现“查询每个部门工资排名前三的员工”这类需求8.x 的窗口函数就是最简洁的写法。我一般建议选型写 MySQL 8.0 以上版本并安到本地或者某台实验服务器上理由是它同时支持传统 DDL/DML 和新式分析函数报告能覆盖的知识面更完整。安装这一步Linux 环境下常见的做法是直接用 apt 或 yum 拉官方源安装注意把 default-authentication-plugin 设置成 caching_sha2_password 还是 mysql_native_password 要先想好。很多图形化客户端比如老版本的 Navicat对 caching_sha2_password 支持不好连接时报错会让你误以为是密码错了。我一般建议实验环境统一用 mysql_native_password虽然 8.x 默认不是它但兼容性最好避免把时间耗在连不上库这种和环境无关的问题上。如果是 Windows 上做实验官方安装包选 Server only 即可Workbench 可有可无后面用命令行加一个 DBeaver 之类的轻量客户端就够用。选型完成之后实验报告里需要有一小节描述环境。这不是凑字数而是让报告可复现。常见写法是列一张表包括数据库名、版本、操作系统、客户端工具、字符集。字符集建议统一 utf8mb4因为如果实验数据里插入 emoji 或者生僻字utf8mb3 会直接报错这也算报告里的一个加分细节。2.2 E-R 图与关系模式实验报告的“第一张脸”数据库概论实验的第一步几乎都是做概念设计也就是画 E-R 图。很多同学跳过画图直接建表到了写报告的时候发现没东西可写。一个真实的教训是某同学的实验题目是“设计一个图书管理系统数据库”他上来就建了三张表图书、读者、借阅结果写范式分析时发现第三范式根本没法满足因为借阅记录里的读者姓名和图书书名都是冗余的。这是典型的没做 E-R 图导致的后果——实体、属性、联系没理清关系模式自然跟着乱。正确的顺序是先列实体读者读者号、姓名、单位、图书书号、书名、出版社、价格、管理员工号、姓名。再列联系借阅是读者和图书之间的多对多联系而且有属性“借书日期”“还书日期”。“管理员”和“借阅”之间是管理员操作的接触。E-R 图完成后再转换成关系模式转换规则教材里都有多对多联系单独成一个关系属性是两端的主码加联系本身的属性比如借阅读者号、书号、借书日期、还书日期、管理员工号。这一步看起来琐碎但实验报告最核心的评分点就在这里评卷老师先看 E-R 图合不合理再看关系模式转换对不对。画图工具有 ProcessOn、draw.io 都行也可以手画拍照贴进报告。我建议用 draw.io导出的矢量图放进 Word 里清晰不糊。关系模式建议写成如下形式并标注主码和外码读者读者号姓名单位主码读者号图书书号书名出版社价格主码书号借阅读者号书号借书日期还书日期管理员工号主码读者号书号借书日期外码读者号、书号这段内容放在报告里老师一眼就能看出你理解了“实体—联系—关系模式”的转化链路比贴二十张截图管用得多。2.3 建库建表 SQL几条语句背后的字符集与约束设计环境准备好、E-R 图定稿后就到了动手建库建表的环节。建库语句本身很短但有几个选项会在你后续执行实验时变成拦路虎。常见的建库 SQL 我一般写成下面这样CREATE DATABASE library_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE library_db;逻辑说明指定 utf8mb4 字符集是为了支持中文和特殊符号_general_ci 是大小写不敏感的排序规则适合课程实验里模糊匹配的需求。如果不指定 COLLATE某些情况下表连接时比对字符串会带来排序规则冲突的报错提前统一能省掉很多麻烦。建表语句需要在字段后面直接把约束写上。图书表、读者表、借阅表三张核心表可以这么写CREATE TABLE reader ( reader_id CHAR(8) PRIMARY KEY, reader_name VARCHAR(20) NOT NULL, unit VARCHAR(50) ) ENGINEInnoDB; CREATE TABLE book ( book_id CHAR(10) PRIMARY KEY, book_name VARCHAR(50) NOT NULL, publisher VARCHAR(30), price DECIMAL(6,2) CHECK (price 0) ) ENGINEInnoDB; CREATE TABLE borrow ( reader_id CHAR(8), book_id CHAR(10), borrow_date DATE, return_date DATE, admin_no CHAR(6), PRIMARY KEY (reader_id, book_id, borrow_date), FOREIGN KEY (reader_id) REFERENCES reader(reader_id), FOREIGN KEY (book_id) REFERENCES book(book_id) ) ENGINEInnoDB;逻辑说明CHAR 和 VARCHAR 的选择要讲得出道理主码用定长 CHAR 是为了检索速度姓名这类变长字段用 VARCHAR 节省空间。DECIMAL(6,2) 表示价格最多 9999.99范围对图书定价够用。CHECK 约束在 MySQL 8.0.16 之后才真正生效如果你用的是 8.0 以下版本这个约束会被忽略但也不报错这一点可以在报告的“实验环境”里注明反而体现你对版本差异的了解。ENGINEInnoDB 是必须写的因为外码约束只有 InnoDB 支持MyISAM 建外码语句执行后既不报错也不生效属于比较隐蔽的一个坑。3. 数据填充与 SQL 查询实验把报告从“能跑”做到“能讲”3.1 用 INSERT 和 UPDATE 构造数据时要注意外码顺序实验数据怎么填是很多人忽略但实际最花时间的环节。如果按“先读者、再图书、最后借阅”的顺序插入就不会触发外码冲突但如果先插入借阅记录MySQL 会直接报 foreign key constraint fails。我看到有同学在这个报错上卡了半小时最后才发现是插入顺序问题。常见做法是把插入脚本拆成三段分别对应三张表并且用显式事务包起来保证中途出错可以整体回滚。插入语句示例如下START TRANSACTION; INSERT INTO reader VALUES (R001, 张三, 软件工程系), (R002, 李四, 软件工程系), (R003, 王五, 计算机科学系); INSERT INTO book VALUES (B001, 数据库系统概论, 高等教育出版社, 45.00), (B002, 计算机网络, 电子工业出版社, 39.50); INSERT INTO borrow VALUES (R001, B001, 2024-03-01, NULL, A01), (R002, B002, 2024-03-02, 2024-03-16, A02); COMMIT;逻辑说明NULL 放在 return_date 表示该书还没归还这是用 NULL 表达“当前未还”的常见做法。事务的目的不是炫技而是让你在不完全确定数据正确时能快速回滚重来。如果你在实验报告里写了“通过事务保证数据一致性”评卷老师会默认你理解了事务的原子性这是第一个能和别人拉开差距的细节。INSERT 本身不是实验的难点难点在于后续的 UPDATE 和 DELETE 会不会受外码限制。比如你想删掉 R001 这位读者但 borrow 表里还有他的借阅记录DELETE 会报错。常见的解决办法是先处理借阅记录再删读者这也正好是实验报告里“数据的完整性控制”这一节的素材。3.2 单表查询与多表连接必写的几个查询类型数据库概论的实验要求里单表查询、连接查询、嵌套查询、集合查询基本是标配。很多报告把三条 SELECT 语句一贴就完事缺少对每种查询类型的意图说明。我建议报告里按查询类型分小节每类写一条有代表性的语句并解释执行过程。比如单表查询里最值得写的是带聚合函数和 GROUP BY 的查询SELECT unit, COUNT(*) AS reader_count FROM reader GROUP BY unit HAVING COUNT(*) 2;逻辑说明WHERE 在分组前过滤HAVING 在分组后过滤这是教科书上的原话但只有你亲手写过一条 HAVING 语句才会理解分组后过滤到底是什么场景。上面这条语句的语义是“找出读者数不少于 2 人的单位”去掉 HAVING 条件结果会完全不同。连接查询是重点因为多表连接涉及连接类型的选择。最典型的一道实验题是“查询借阅了图书的学生姓名和书名”写出下面的语句只是及格水平SELECT reader.reader_name, book.book_name FROM borrow JOIN reader ON borrow.reader_id reader.reader_id JOIN book ON borrow.book_id book.book_id WHERE borrow.return_date IS NULL;逻辑说明JOIN 的顺序是从 borrow 出发先连读者得到谁借的再连图书得到借的什么。WHERE borrow.return_date IS NULL 表示筛选未归还的记录。如果你想在报告里体现对连接的理解还可以补一句为什么没选 LEFT JOIN因为内连接只返回借阅表里匹配成功的记录左连接会把没有借阅记录的读者也带出来两种结果集的口径完全不同写报告时要把这种取舍写清楚。嵌套查询和 EXISTS 也建议各写一条。比如“查询没有借阅过任何图书的读者”IN 和 NOT EXISTS 两种写法均可以但 NOT EXISTS 在语义上更贴近“不存在”的判断性能在数据量大时也通常优于 NOT IN因为 NOT IN 遇到 NULL 值会返回空结果。这个坑非常经典值得写进实验报告成为你的“思考题”部分。4. 进阶实验视图、事务、存储过程与触发器怎么选型4.1 视图实验为什么我们建视图而不直接查表实验报告里通常要求建一两个视图。很多同学不理解视图的意义觉得就是把一条 SELECT 语句存起来多此一举。实际差别在于视图提供了逻辑独立性、安全性控制和简化查询三层价值。比如给辅导员查“本系学生的借阅情况”如果直接给基表的查询权限他就能看到所有学生的所有数据但建一个视图把它限定在“软件工程系”范围内再授 SELECT 权限给他他就只能看该看的数据。CREATE VIEW v_se_student_borrow AS SELECT reader.reader_name, book.book_name, borrow.borrow_date FROM borrow JOIN reader ON borrow.reader_id reader.reader_id JOIN book ON borrow.book_id book.book_id WHERE reader.unit 软件工程系;逻辑说明视图本身不存储数据执行查询时临时做连接运算所以视图并没有“空间换时间”的效果。这一点经常被误解。数据量大的时候基于视图的查询不一定比直接查基表快它主要是逻辑层的东西这一点写进报告还能体现你的理解深度。视图能不能更新取决于它是否满足“可更新视图”的条件比如不能有 GROUP BY、DISTINCT 这类破坏行对应关系的元素很多同学在视图上跑 UPDATE 失败后以为是自己写错了其实这是 MySQL 对更新规则的限制。4.2 事务与隔离级别实验报告里最容易被追问的一块事务实验是数据库概论的重点也是面试常考。建议实验里用两个终端窗口连同一个数据库模拟两个并发事务。最经典的实验是“买书扣库存”场景将图书数量减 1 和插入借阅记录放在一个事务中。两个终端同时执行观察不同隔离级别下的行为差异。我第一次带实验时就让 A 同学开了两个会话分别执行下面的逻辑-- 会话 1 START TRANSACTION; UPDATE book SET price price 10 WHERE book_id B001; -- 此处不提交等会话 2 SELECT * FROM book WHERE book_id B001;-- 会话 2 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT price FROM book WHERE book_id B001;现象是会话 2 在 READ UNCOMMITTED 下读到了会话 1 尚未提交的修改也就是脏读。把隔离级别改成 READ COMMITTED 后会话 2 读到的还是旧值脏读消失。这个对照实验写在报告里非常有说服力因为它直观地展示了隔离级别存在的意义。但要注意MySQL 默认隔离级别是 REPEATABLE READ不是 READ COMMITTED实验时把会话 2 的隔离级别显式设置成不同值并记录结果才是完整的实验。下面是一个查看隔离级别的语句SELECT transaction_isolation;逻辑说明这条语句返回当前会话的事务隔离级别。做实验前先执行它确认环境状态再做并发测试避免因默认值不同导致现象解释出错。实验报告里还应该写上结论隔离级别越高一致性越好并发能力越弱脏读、不可重复读、幻读三种异常分别对应 READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 的不设防点MySQL 的 REPEATABLE READ 通过 MVCC 解决了幻读的大部分场景但间隙锁仍在特定条件下发挥作用。4.3 存储过程和触发器要不要写进报告如果实验大纲要求了存储过程或触发器那么选哪个场景写才最有说服力我建议选一个“需要被重复调用的业务规则”比如批量登记借阅记录。存储过程的好处是逻辑封装在数据库端客户端不需要重复写多行 SQL。触发器则适合做“自动校验”的场景比如借书时必须保证该读者当前未借满 5 本超过则拒绝。下面这个触发器可以作为样例DELIMITER // CREATE TRIGGER trg_borrow_limit BEFORE INSERT ON borrow FOR EACH ROW BEGIN DECLARE cnt INT; SELECT COUNT(*) INTO cnt FROM borrow WHERE reader_id NEW.reader_id AND return_date IS NULL; IF cnt 5 THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT borrow limit exceeded; END IF; END// DELIMITER ;逻辑说明NEW.reader_id 表示待插入的新行中的读者号BEFORE INSERT 表示在插入前做校验SIGNAL SQLSTATE 45000 是 MySQL 主动抛异常的标准写法如果不用它触发器只能静默失败调用方完全不知道发生了什么。写报告时值得加一句触发器有额外开销而且在数据量大的批处理场景里会成为性能瓶颈生产环境里很多团队宁可在应用层做同样的事但在课程实验里写触发器可以充分展示触发器的机制理解。5. 实验路上的常见坑与排查清单现象、原因、解法5.1 中文乱码和数据插不进去现象插入中文后 SELECT 出来是问号 ?或者 INSERT 语句直接报错 “Incorrect string value”。原因客户端连接字符集不是 utf8mb4或者数据库、表的字符集不统一。最常见的是数据库建库时设了 utf8mb4但客户端工具连接用的还是 latin1。解决建库时统一写 CHARACTER SET utf8mb4连接后在执行插入前先执行 SET NAMES utf8mb4用命令行连接时加 --default-character-setutf8mb4 参数。字符集问题排查可以一次到位不建议在报告里只贴一张“乱码截图”了事要把排查链路写清楚。5.2 外键插入失败foreign key constraint fails现象向从表插入数据时 MySQL 报错但检查数据感觉没问题。原因最常见的是从表引用的主表主码值不存在或者两张表的字段类型不一致。比如 reader_id 在主表是 CHAR(8)在从表却定义成了 VARCHAR(8)虽然看起来能关联但 MySQL 对外键字段类型要求严格字符集的差异也会导致这个错误。解决先查主表中对应字段是否存在该值再核对字段类型和字符集完全一致。排查时用下面这条语句查看表结构定义重点比较两个字段的类型定义SHOW CREATE TABLE borrow;逻辑说明这条语句会返回完整的建表语句外键定义、字符集、约束一眼就能看到。用它排查比反复猜效率高得多。5.3 NOT IN 与 NULL 值导致查询结果为空现象查询“没借过书的读者”返回结果为空明明是空表却有数据。原因borrow 表里只要有任意一个读者号为 NULLNOT IN 子查询的结果集里包含 NULL整个 NOT IN 条件就不会返回任何行。这是 SQL 三值逻辑的体现很多同学第一次遇到都很难理解。解决子查询过滤掉 NULL比如 NOT IN (SELECT reader_id FROM borrow WHERE reader_id IS NOT NULL)或者换成 NOT EXISTS 写法不存在 NULL 问题。这一现象很适合写进报告的“实验心得”部分能明显提升报告的非机械感。5.4 DELETE 基表数据被外码约束拒绝现象删除某个读者时 MySQL 报错 Cannot delete or update a parent row: a foreign key constraint fails。原因从表存在引用该读者的记录外码约束阻止了主表删除。解决按依赖顺序处理先删除或更新从表的相关记录再删主表或者在设计外键时指定 ON DELETE CASCADE。课程实验里不推荐用 CASCADE因为可能造成意外的大批量删除报告里写清楚“通过先删子表数据维护了引用完整性”反而是加分项。5.5 排序规则冲突Illegal mix of collations现象两张表连接时报错涉及字段的排序规则不一致尤其出现在不同字符集下。原因建表时有的字段用了 utf8mb4_general_ci有的用了 utf8mb4_unicode_ci内连接时 MySQL 无法自动兼容。解决统一所有表字段的排序规则。查看字段排序规则用 SHOW FULL COLUMNS FROM 表名修改字段排序规则用 ALTER TABLE ... MODIFY。6. 把实验报告写“厚”从提交物到可翻查的积累很多同学实验做完了报告却只写几段 SQL 和几张截图有点浪费。因为数据库实验报告是少有的能同时覆盖业务设计、SQL 编写和故障排查三块内容的课程作业。我觉得至少应该补三样东西一是数据字典表把所有表名、字段名、类型、约束、说明列成表这相当于一个小型接口文档二是关键查询的“验证方法”即每写完一条复杂 SQL要写清楚你如何确认结果是正确的比如对聚合结果做手工核算、用 LIMIT 抽查数据等三是结合实验数据写一个分析结论比如“从借阅表中统计热门出版社”这种带业务视角的小结论让报告不只是操作记录。这三样补进去之后报告才是完整的。另外建议保留每次实验的原始命令和报错截图存成一个“排错记录”文件夹。哪怕实验已经交掉过半年面试前翻出来你会发现这些真实报错比任何面经都记忆深刻。我自己就有这个习惯某次实验里 NOT IN 和 NULL 的坑让我在一次面试手写 SQL 时躲过了同样的陷阱。希望这个建议也能帮到你实验不只是为了学分把每次踩坑都转化成自己的技术沉淀后面你会感谢当初这份认真的自己。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站