资讯详情

数据库系统概论课程设计实验报告指南:从E-R图、关系模式到SQL实现

📅 2026/10/11 19:25:55 | 华诺云谱 👁 阅读
数据库系统概论课程设计实验报告指南:从E-R图、关系模式到SQL实现
简介资源为山东科技大学计算机科学与技术专业《数据库系统概论》课程设计实验报告主题是设计并实现一个支持创建表与修改表的简单DBMS适合高校数据库课程学习者、课程设计撰写者及对数据库底层实现感兴趣的读者。报告完整覆盖CREATE TABLE与ALTER TABLE语句的实现思路涉及表的物理存储结构设计、数组形式存储表结构信息、命令行与图形化界面两种交互方式并配有程序流程图。通过解析标准SQL语句、创建表文件、持久化存储与提取表信息等关键环节清晰呈现数据库管理系统的基本组成与工作机理。包体为1份408KB的doc文档包含课程设计任务书、需求分析、设计思想、主要结构以及保存与提取表信息的实现方案既可用于理解数据库系统核心功能也可作为课程设计报告体例的参考范本。已有931人学习下载适合需要高效完成数据库课程设计或希望了解简单DBMS搭建思路的人群。1. 一份数据库系统概论课程设计实验报告到底要证明什么期末周把数据库系统概论课程设计实验报告这个题目摊在桌上时大多数人慌的不是 SQL 不会写而是不知道这份报告要写到什么程度才算完。数据库系统概论这门课评分的重心从来不是你写了多少页而是你能不能证明自己做的系统真的对了——需求分析有没有落到建表语句E-R 图和关系模式是否一致代码能否在干净环境重跑每个功能有没有输入输出证据。我见过不少同学把代码截图贴得满满当当最后却被一句图和表对不上打回。这篇文章就把这条从需求到报告的完整链路拆开讲从报告骨架、E-R 图设计、SQL 落地到避坑清单。不管你是山东科技大学选的选课系统、图书管理系统还是其他学校的相近题目这条链路都能直接套用如果你手头用的是第六版教材原理部分一样适用。2. 先定报告骨架把实验报告拆成五个可交付板块写实验报告最忌讳一上来就写系统介绍。课程设计实验报告本质是一份技术证据链老师应该能沿着你的报告把整个设计过程还原出来。先确定板块再想每一块填什么比边写边想高效得多也更不容易漏项。2.1 一份能过审的实验报告由哪五个板块构成我把课程设计实验报告拆成五个板块每个板块对应一个明确的产出物板块必须包含的产出物常见错误做法需求分析用户角色、业务规则、功能清单、数据流描述写两页系统背景没有一条可验证的规则概念结构设计E-R 图、实体/属性/联系说明只贴一张图不解释联系的方向与基数逻辑结构设计关系模式、主键外键、范式分析把建表 SQL 直接堆进来不写设计理由物理设计与实现建库建表 SQL、索引、视图、存储过程、关键代码全部放截图代码无法复制重跑测试与总结功能测试用例、性能对比、遗留问题清单用系统运行良好一笔带过这五个板块在老师眼里分别对应五个问题你知不知道自己在做什么系统、你会不会建模、你懂不懂关系数据库理论、你能不能动手实现、你的结论有没有证据支撑。缺了任何一个报告都会变成代码附录而不是实验报告。提示先跑通全部代码再回头补报告不要边写报告边改代码。截图和 SQL 只有在代码稳定后才能保持一致。2.2 需求分析怎么写才算到位从用户故事到业务规则常见做法是先列功能清单比如学生可以选课教师可以录入成绩。但功能清单只写了能做什么没有写在什么约束下做而课程设计考核的重点恰恰在约束上。以选课系统为例需求分析至少要写出三类用户角色学生、教师、教务管理员。角色一旦定了后续的权限设计、功能划分都跟着它走。业务规则是需求分析里最容易拿分的部分每条规则都要能对应到后续的建表语句或存储过程。我一般这样组织业务规则实现手段在报告中写的位置同一学期不能重复选同一门课enrollment 表组合主键stu_id, course_id, term逻辑结构设计课程选课人数上限 60 人存储过程或触发器里 COUNT 后判断物理实现成绩修改留痕建 score_log 表 AFTER UPDATE 触发器物理实现退课后历史成绩可查成绩单独存成绩表选课表只存当前学期记录逻辑结构设计需求分析写到这个颗粒度后面建表时基本不会出现这个字段到底要不要的纠结。每一条规则都是来自真实业务场景的约束。反过来如果先建表再补需求很容易出现需求写了一套表结构不支持的尴尬。2.3 报告的三个评分锚点文档一致性、代码可跑、过程证据写报告前先定三个验收标准每一章写完都拿它们自查。第一个是文档一致性E-R 图、关系模式、建表语句三者必须来自同一个设计任何一处改动都要三处同步老师最喜欢拿 E-R 图对着建表 SQL 找外键一找一个准。第二个是代码可跑报告里的 SQL 应当能按出现顺序在一个空库中完整执行最低要求是复制粘贴就能跑不能因为自己电脑上能跑就省略前置依赖。第三个是过程证据每个功能点要有一组输入数据、操作步骤和输出截图比如选课人数满员拒绝就要有第 61 条选课记录被拒的截图。我的习惯是画一张自查表E-R 图里每个联系是否都有对应的外键每张表的每个字段是否都能在需求分析里找到出处每个截图有没有覆盖到字段值。这张表不放进报告但它决定了报告能不能过审。写报告时也尽量做一个空库重放的脚本目录把建库、建表、视图、存储过程、测试数据分开存放每完成一版就在全新数据库里跑一遍再贴进报告。3. 从概念模型到关系模式设计阶段如何不返工E-R 图是数据库系统概论课程设计里最值钱的一张图它是理论到实践的桥梁。很多同学把它画成框加线就交了到建表时才发现这个联系到底是 1:N 还是 M:N决定了要不要单独建一张表稍不留神就得推翻重来。3.1 E-R 图设计实体、属性、联系怎么定实体识别有一条很实用的判断规则一个对象如果独立存在、且需要维护自己的唯一标识它就是实体如果某个信息只有在两个实体发生关系时才产生意义那它就是联系的属性而不是实体的属性。以选课系统为例学生、课程、教师是实体而成绩既不属于学生也不属于课程它属于学生选修课程这一次行为所以成绩要放在选课联系上而不是学生表或课程表的字段。系别是另一个常见误区。如果系统只需要知道学生属于哪个系那系别作为学生的一个属性就够了只有当系统要维护系别本身的独立信息比如系主任、办公电话、成立时间才需要把系别提升为实体。判断时可以问自己两个问题这个对象有唯一标识吗它的信息会被其他多处引用吗两个答案都是是才考虑实体化。否则坚持能当属性就不当实体的原则E-R 图不会越画越臃肿。3.2 E-R 图转关系模式主键、外键、联系表的转换规则E-R 图转关系模式的规则是课程设计报告里必须写清楚的一段标准做法如下联系类型转换规则例子1:1把任一端主键放入另一端关系做外键联系属性一起并入学生与校园卡账户1:N在 N 侧关系中加入 1 侧主键作为外键一个教师开多门课课程表加教师号M:N单独建一张联系表主键为两端外键的组合学生选课建 enrollment 表以选课系统为例学生与课程是 M:N转出来就是 enrollment 表主键用stu_id, course_id, term成绩这样的联系属性必须放在这张联系表里不能挂在学生表或课程表上。教师与课程是 1:N教师号作为外键放在课程表同时课程表加一个 KEY idx_teacher 指向教师号。这里有一个容易忽略的细节M:N 联系表的主键顺序会影响索引效率。当查询总是先给学号再查课程时把 stu_id 放在联合主键的第一位走主键索引会更顺。3.3 范式分析为什么课程设计一般停在 3NF范式分析是报告里体现理论深度的地方。先看一个反例如果 enrollment 表里出现 teacher_name那么 teacher_name 依赖 teacher_id而 teacher_id 本身不是主键的一部分这就形成了传递依赖不满足 3NF会出现数据冗余和更新异常——同一个老师换职称所有选课记录都要改一遍。规范化按三步走。1NF 要求字段不可再分成绩不能存成平时分/期末分一个字符串要拆成两个字段2NF 要求非主属性完全依赖主键成绩依赖整个组合主键所以留在 enrollment3NF 要求非主属性之间不能有传递依赖教师职称不该出现在课程表里要放到教师表。每一步都对应一个设计决策报告里写清楚为什么这样拆比直接写达到 3NF有说服力。不过范式不是越高越好。当查询场景是固定的统计报表时可以保留少量冗余字段比如成绩报表里冗余系别避免每次统计都三表连查。在报告里主动写一句这是为了减少 JOIN 代价而保留的冗余已经清楚它的同步风险老师会觉得你理解了范式的代价而不是只会背定义。4. 把关系模式落成可运行代码建库、约束、索引与事务设计阶段再完整最终都要落到一段能跑的 SQL 上。课程设计的代码部分不需要花哨但每段代码都要能自圆其说——为什么用这个数据类型、为什么建这个约束、为什么加这个索引每一处都要禁得起追问。4.1 开发环境怎么选MySQL 8.0 配合 VSCode 的最小配置课程设计最常见的做法是本地装 MySQL 8.0配一个 VSCode 写 SQL 脚本。选 MySQL 的理由很实在免费、单机可跑、教程多语法与数据库系统概论教材里的标准 SQL 接近能覆盖建库建表、约束、索引、视图、存储过程、事务、触发器这些实验点。VSCode 装一个 MySQL 插件在设置里填好连接名、root 密码、端口 3306 和字符集 utf8mb4就能直接在编辑器里跑 SQL 并看到结果面板。一个实操建议不要直接在命令行里粘贴报告中的 SQL而是把全部语句保存成 .sql 脚本文件再整体执行。这样报告里贴出的 SQL 本身就是一份可完整重放的脚本而不是一堆零碎命令。VSCode 里右键脚本选执行返回的结果面板可以直接截图作为报告中的运行证据。4.2 建库建表字符集、存储引擎、外键约束的判定下面是选课系统最小闭环的建库建表脚本注意外键依赖顺序teacher 先建course 才能引用它。-- 建库字符集用 utf8mb4避免中文乱码 CREATE DATABASE IF NOT EXISTS course_sys DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE course_sys; -- 教师表先建因为 course 表引用它 CREATE TABLE teacher ( teacher_id CHAR(8) NOT NULL, teacher_name VARCHAR(20) NOT NULL, title VARCHAR(20) NOT NULL, PRIMARY KEY (teacher_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 学生表 CREATE TABLE student ( stu_id CHAR(10) NOT NULL, stu_name VARCHAR(20) NOT NULL, dept VARCHAR(30) NOT NULL, enroll_year SMALLINT NOT NULL, PRIMARY KEY (stu_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 课程表teacher_id 作为外键属于 1:N 的 N 侧 CREATE TABLE course ( course_id CHAR(6) NOT NULL, course_name VARCHAR(50) NOT NULL, credit DECIMAL(3,1) NOT NULL, hours SMALLINT NOT NULL, teacher_id CHAR(8), PRIMARY KEY (course_id), KEY idx_teacher (teacher_id), CONSTRAINT fk_course_teacher FOREIGN KEY (teacher_id) REFERENCES teacher(teacher_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 选课表组合主键实现同一学生同一课程同一学期只选一次 CREATE TABLE enrollment ( stu_id CHAR(10) NOT NULL, course_id CHAR(6) NOT NULL, grade DECIMAL(4,1) NULL DEFAULT NULL, term VARCHAR(11) NOT NULL, enroll_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (stu_id, course_id, term), KEY idx_course (course_id), CONSTRAINT fk_enroll_student FOREIGN KEY (stu_id) REFERENCES student(stu_id) ON UPDATE CASCADE, CONSTRAINT fk_enroll_course FOREIGN KEY (course_id) REFERENCES course(course_id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字符集和校验规则要在建库时就定好utf8mb4 是四字节编码能存 emoji 和生僻字utf8 在 MySQL 里最多三字节存某些输入时会报错。课程设计阶段不设置答辩时被问到中文乱码怎么解决会很难收场。学号、课程号这类编号用 CHAR 定长类型因为编号长度固定定长字段在主键索引里比 VARCHAR 更省空间学分和成绩用 DECIMAL 定点数不用 FLOAT避免出现 88.5 存成 88.4999 的问题。enrollment 表的主键设计值得在报告里单独解释把 term 加入联合主键之后同一学期不能重复选同一门课就不再依赖应用层判断而是数据库层面的硬约束。外键的两个级联策略也要说明用途ON UPDATE CASCADE 表示学号被修改时选课表跟着改ON DELETE CASCADE 表示课程被删除时选课记录自动清理。如果你选的业务规则要求退课后保留历史成绩就不能把成绩放在 enrollment而是要单独建一张 score 表。4.3 索引与视图用 EXPLAIN 验证索引有没有生效索引部分是实验报告里最容易拿到的加分项因为可以直观地展示前后对比。给成绩字段建一个索引然后用 EXPLAIN 看执行计划-- 成绩范围查询是选课系统里最常见的查询给 grade 建索引 CREATE INDEX idx_enroll_grade ON enrollment(grade); -- 用执行计划确认索引是否被用到 EXPLAIN SELECT * FROM enrollment WHERE grade BETWEEN 80 AND 90 AND term 2024-2025-1;EXPLAIN 输出里key 字段如果显示 idx_enroll_grade说明索引被命中type 会从 ALL 变成 rangerows 会从全表行数降到几十行。把这段输出截图放进报告的物理实现部分比任何文字描述都硬。注意一个常见误区索引不是越多越好每个索引都会拖慢写入速度。课程设计里通常只在两类字段上建频繁作为 WHERE 条件的字段以及外键字段。视图部分可以做一个成绩查询视图把三表连查封装起来-- 视图固定封装三表连查简化报告中重复 SQL CREATE VIEW v_student_score AS SELECT s.stu_id, s.stu_name, c.course_name, e.grade, e.term FROM student s JOIN enrollment e ON s.stu_id e.stu_id JOIN course c ON e.course_id c.course_id;视图的本质是保存的查询定义不是数据拷贝。报告里要主动写清楚视图不提升查询速度只简化查询语句否则老师一追问视图为什么让查询变快了答不上来反而扣分。4.4 存储过程与事务为成绩录入写一个带回滚的流程课程设计里最容易用上事务的场景是成绩录入一门课几十条成绩批量提交中间任何一条失败都不能让前面的写入生效。下面这个存储过程演示了事务、异常处理和不存在则插入的完整逻辑。DELIMITER // CREATE PROCEDURE add_grade( IN p_stu_id CHAR(10), IN p_course_id CHAR(6), IN p_grade DECIMAL(4,1), IN p_term VARCHAR(11) ) BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT grade update failed, transaction rolled back; END; START TRANSACTION; UPDATE enrollment SET grade p_grade WHERE stu_id p_stu_id AND course_id p_course_id AND term p_term; IF ROW_COUNT() 0 THEN INSERT INTO enrollment(stu_id, course_id, grade, term) VALUES (p_stu_id, p_course_id, p_grade, p_term); END IF; COMMIT; END // DELIMITER ; -- 调用S20220001 同学 2024-2025-1 学期数据库课程成绩 88.5 CALL add_grade(S20220001, C001, 88.5, 2024-2025-1);这个存储过程要讲清楚三个点START TRANSACTION 到 COMMIT 之间是一个原子单位任何一步失败都能回滚DECLARE EXIT HANDLER 捕获所有 SQL 异常先回滚再向外抛错误信号ROW_COUNT() 判断 UPDATE 是否命中行避免先 SELECT 再 INSERT的竞态。这段过程中没有显式设置隔离级别用的是 MySQL 默认的 REPEATABLE READ报告里补一句课程设计场景用默认隔离级别即可生产环境才需要按业务调整就能显示出你清楚隔离级别的边界。5. 实验报告常见问题五个翻车现场与排查顺序写课程设计实验报告时翻车通常不是翻在技术上而是翻在文档与代码脱节和证据不足上。下面五个问题是我见过最多的情况每条都按现象、原因、解决的顺序拆开。5.1 E-R 图和建表 SQL 对不上答辩当场被问住现象老师翻开报告指着 E-R 图里教师-课程的联系问外键在哪代码里 course 表根本没有 teacher_id 字段只能支支吾吾说漏了。原因先写了 SQL后来补 E-R 图图是照着感觉画的没有逐条核对导致图和表各说各话。解决把 E-R 图当作唯一设计源头改设计先改图再改关系模式最后改建表 SQL。提交前用三列清单核对一遍每个联系类型、对应的关系表、外键字段名。课程设计的图与表不一致比图丑严重得多老师的第一判断就是逻辑没有闭环。5.2 报告里的 SQL 复制出来跑不通现象老师或同学按报告顺序把 SQL 粘到 MySQL第一句就报错常见错误是near s...或者Table already exists。原因报告里贴的是带行号和箭头的客户端截图复制时把-符号一起带进去了或者脚本依赖的视图、存储过程还没创建就被提前调用再或者连库语句被放在最后一段。解决交付前做一次空库重放验证——先 DROP DATABASE course_sys再按报告里的顺序从头执行所有 SQL。建立一个可重跑脚本目录把建库、建表、索引、视图、存储过程、测试数据分开保存验证通过之后再贴进报告。5.3 触发器、存储过程写了却被判炫技现象报告里放了三个触发器老师问这个触发器什么时候触发、和存储过程有什么区别答不上来。原因把高级特性当成了凑页面数量的工具写之前没有想清楚它到底保住了哪条业务规则导致答辩时只能背定义。解决每个高级特性绑定一条写进需求分析的规则。比如成绩更新要留痕就建 score_log 表和 AFTER UPDATE 触发器选课人数上限 60 人就写 BEFORE INSERT 触发器做 COUNT 判断。报告里的顺序是先写规则再写触发器最后写验证截图逻辑链自然就完整了。5.4 多窗口更新成绩事务和隔离级别没设置现象测试时开着两个查询窗口同时改同一条成绩最后值不是预期结果报告里写并发安全却被老师质疑。原因存储过程没有显式声明隔离级别自动提交开着异常处理也没覆盖全部场景。解决在存储过程的 START TRANSACTION 之前用SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;显式声明隔离级别并在报告里说明为什么课程设计场景选它。同时把两个并发窗口的执行结果截图放进测试章节比口头保证更有说服力。5.5 测试数据太少性能对比没有参考价值现象建了索引报告写查询效率提升 50%但测试表里只有 30 行数据两次查询耗时差是随机波动。原因数据量没有超过索引生效的阈值优化前后的时间差没有统计意义。解决测试数据至少几千行可以用存储过程循环 INSERT 生成实验数据。性能对比表里把数据行数、是否命中索引、执行计划截图一起放上。没有数据量的性能测试本质上是要么主观臆断要么是自欺欺人。整个报告从头到尾检查的顺序应该是先查设计链路——需求到 E-R 到关系模式到 SQL 是否同源再查可重跑性——清库重放一遍然后查功能证据——每个功能点有没有输入输出截图最后查性能数据——测试数据量够不够。按这个顺序走一遍绝大多数被打回的问题都能提前暴露。6. 用一次最小压力测试把报告收尾在实验数据上报告的最后不能以系统运行良好收场那叫没结论。常见做法是加一轮最小压力测试用并发请求跑一组数据把报告从文档变成实验记录。下面是一个 Python 脚本模拟 50 个并发会话做随机只读查询统计耗时和 QPS。执行前先pip install pymysql并把测试数据量撑到 500 个学生 × 20 门课以上。import pymysql import random import threading import time conn_info { host: 127.0.0.1, user: root, password: 123456, database: course_sys, charset: utf8mb4, } stu_ids [fS{i:04d} for i in range(1, 501)] course_ids [fC{i:04d} for i in range(1, 21)] queries_per_thread 100 def read_worker(): conn pymysql.connect(**conn_info) try: with conn.cursor() as cur: for _ in range(queries_per_thread): stu random.choice(stu_ids) course random.choice(course_ids) cur.execute( SELECT grade FROM enrollment WHERE stu_id%s AND course_id%s AND term2024-2025-1, (stu, course), ) conn.commit() finally: conn.close() threads [threading.Thread(targetread_worker) for _ in range(50)] start time.time() for t in threads: t.start() for t in threads: t.join() elapsed time.time() - start total len(threads) * queries_per_thread print(f线程数50, 查询总数{total}, 耗时{elapsed:.2f}s, QPS≈{total / elapsed:.0f})脚本用随机读而不是写入来压测因为写入并发会引入锁竞争和脏数据干扰对查询性能的判断。测试要跑两轮第一轮是没建索引的基线第二轮建完 idx_enroll_grade 之后再跑。两轮结果整理成一张对比表放进报告表里的数字必须是你自己实测得到的。测试场景数据行数总查询数耗时(s)QPS执行计划情况无索引基线1000050008.4595typeALL, rows10000建 grade 索引后1000050002.12381typerange, keyidx_enroll_grade报告中每个数字都要能对应一条可重复的操作。老师问这个 8.4 秒怎么来的时你能当场重新跑一遍脚本给它看这份实验报告就从文档变成了实验记录。以前我自己写课程设计时把大量篇幅花在系统介绍上被老师批了一句我要看的是实验不是产品说明书。后来每次做课程设计我都先搭好数据生成脚本和压测脚本再倒过来补报告结论全部从测试记录里抄。这个习惯让答辩从背诵 PPT 变成了现场讲数据。如果你也正在写数据库系统概论课程设计的实验报告别急着截图先把证据链铺好。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑