☰
数据库系统MIS开发实验:从ER图设计到SQL实现的全流程实战
2026/10/14 13:53:37 网站建设 项目流程

简介:南京邮电大学《数据库系统》课程的小型MIS开发实验报告(实验三),面向数据库初学者及高校计算机专业学生,完整呈现航班信息管理系统的数据库设计全流程。报告从实验目的与要求出发,基于所选DBMS环境,详细给出了创建fightds数据库、设计并创建航班信息表、飞机信息表、乘客信息表和机票表四张数据表、添加唯一索引、插入示例数据、创建视图以及编写存储过程查询航班余票等完整SQL语句,并附有实验硬件软件环境、实验原理及内容说明,可帮助读者快速理解C/S或B/S结构系统的开发思路,掌握ODBC与ADO访问数据库的方法要点,为独立完成同类课程设计提供直接参考。资源包内为1个doc格式文档,大小约372KB,文档结构清晰紧凑。目前已有241人学习下载,适合需要准备数据库实验报告或复习数据库系统设计内容的在校学生参考。

1. 数据库系统小型 MIS 开发实验:它不是编程大作业,而是一次全流程数据库设计实战

南京邮电大学数据库系统小型 MIS 开发实验,看起来是“用某门语言写一个管理系统”,实际上拼的是你把《数据库系统概论》里的概念设计、逻辑设计、物理设计完整走一遍的能力。评分重点不在界面有多炫,而在 ER 图画得规不规范、关系模式能不能过 3NF、SQL 里有没有用视图和存储过程、触发器有没有真的挡住非法数据,以及实验报告能不能自圆其说。这个实验适合正在被课设折磨的学生,也适合想补一遍数据库设计方法论的开发者——哪怕你工作多年,回头把这一套流程走扎实,写 SQL 的手感都会不一样。

我见过两种极端:一种是抱着“写代码就行”的心态,把实体直接拍成表,结果答辩时被老师一眼看穿范式问题;另一种是过度设计,一个图书管理系统做了十几个表、几十个触发器,把自己绕进去。正确的姿势是把它当成一次微型企业级项目来推演。下面按我做这类实验的习惯,从选题一路拆到报告成稿。

2. 选题与需求分析:先定边界再动手,ER 图画错了后面全白做

2.1 怎么选题:实体数控制在 6 个以内,业务规则要清晰

这类实验的选题方向通常很宽:学生选课、图书借阅、教材征订、实验室预约、宿舍报修、社团活动管理,名字换一换,本质都是“人员 + 资源 + 记录”。我一般会劝你选业务规则最清晰的,而不是看起来最有创意的。原因很简单,规则越清晰,评分点越好量化。

以我做过的学生选课系统为例,核心实体就四五个:学生、教师、课程、班级、选课记录。业务规则是:学生选课,同一门课只能选一次;课程有选课人数上限;教师可以带多门课程;选课后记录平时成绩和期末成绩。这套规则在《数据库系统概论》第六章的例题里几乎有现成的影子,画 ER 图和写 SQL 都有参照,不容易走偏。

选型时有一个硬指标:实体数控制在 6 个以内。实体太多,联系也跟着多,M:N 联系一多,关系模式转换的工作量成倍上涨。课设周期就几周,你还要留时间写报告和做演示,实体数超过 6 个基本意味着后期要在 ER 图和范式检查上反复返工。如果你实在想做一个“大一点”的系统,先砍业务,把“在线支付”“多角色权限”这类企业级需求全部去掉,课设题目只需要一个角色能干活就行。

2.2 需求分析:把一句“做个选课系统”拆成可落地的数据流

很多人直接跳过需求分析去画 ER 图,等到建表时才发现“成绩该放哪张表”这种问题没想清楚。我一般会先输出一份一页纸的需求说明,不需要写用例图、泳道图,就用文字把以下三块讲清楚。

角色清单:管理员负责维护学生、教师和课程信息;学生选课和退课;教师录入成绩。业务清单:登录、选课、退课、查成绩、按课程统计选课人数。数据项清单:每张表里打算放哪些字段,比如学生要有学号、姓名、性别、年龄、班级号、院系;课程要有课程号、课程名、学分、授课教师工号。这份清单不用写得太正式,但一定要能回答一个问题:系统里有哪些“东西”,每个“东西”需要记哪些“属性”。

数据流用文字描述即可:学生登录系统 → 查看可选课程列表 → 点击选课 → 系统校验是否重复选课、课程是否满员 → 写入选课记录 → 教师登录后能看到选课学生名单 → 录入成绩。这一串文字描述在实验报告的“系统功能说明”一节里原样搬进去就够用。把数据流写清楚的优势是,后面画 ER 图时,每个联系都能在数据流里找到对应业务动作,不会画出逻辑上说不通的线。

2.3 从需求到 ER 图:实体、属性、联系的画法与会踩的第一个坑

ER 图的标准画法教科书里都有:实体用矩形,属性用椭圆,联系用菱形,1:1、1:N、M:N 标注在联系的两端。选课系统里的四条核心联系:班级与学生是 1:N(一个班有多个学生),教师与课程是 1:N(一个教师教多门课),学生与课程是 M:N(一个学生选多门课,一门课被多个学生选),课程与选课记录是 1:N。画完之后数一遍,每个联系都能说清“是哪个业务动作产生的”,这就合格了。

这里有一个必修课:M:N 联系必须转换成单独的关系模式。这是实验报告的高频扣分点,也是新手最容易犯的错。很多人把选课记录直接画成课程表的一个属性,后面建表时外键关系就乱了。正确的做法是让“选课”成为一个独立实体,属性包括学号、课程号、成绩、学期,然后它的主键是学号和课程号拼成的联合主键。

画 ER 图用的工具没有硬性要求,draw.io 免费且模板全,MySQL Workbench 的 EER 图能直接从建表脚本逆向生成,但逆向出来的图连线经常歪歪扭扭,不如手画的干净。如果你用的是 PowerDesigner,请检查导出的图片分辨率,PPT 里缩得很小会让老师看不清联系上的基数标注。我自己的习惯是先用纸笔把 ER 图画顺,再搬到工具里,图的布局一定预留足够连线空间,不要让联系线穿过实体框。

3. 关系模式设计:把 ER 图翻译成表结构,3NF 是底线不是追求

3.1 ER 图转关系模式的三条规则

ER 图转关系模式在《数据库系统原理》教材里讲得很细,落地的时候记住三条规则就够。第一条,每个实体转成一张表,实体的属性就是字段,实体标识符就是主键;第二条,M:N 联系转成独立的关系表,联系本身的属性(比如成绩、学期)放进这张表,主键由两端实体主键组合而成;第三条,1:N 联系不需要转表,直接把“1”端的主键下推到“N”端表里当外键。

按这三条规则,选课系统的关系模式可以写成下面这样。学生表 STUDENT(SNO, SNAME, SSEX, SAGE, CLASS_NO, SDEPT),SNO 是主键;教师表 TEACHER(TNO, TNAME, TITLE),TNO 是主键;课程表 COURSE(CNO, CNAME, CREDIT, TNO),CNO 是主键,TNO 引用教师表;选课表 SC(SNO, CNO, GRADE, SEMESTER),SNO 和 CNO 联合主键,两个字段分别引用学生表和课程表。写关系模式时务必用“表名(字段, 字段, …)”这种格式,主键加下划线,这也是实验报告里最标准的要求。

有一个细节容易忽略:课程表里放了教师工号 TNO 作为外键,这个字段来自“教师—课程”的 1:N 联系。如果你画 ER 图时漏了这条联系,建表时就会想不起来加 TNO,等多对多关联查询时才发现课程跟教师连不上。所以关系模式写完之后,一定要对着 ER 图逐条检查:图上的每一根连线,转换后的表结构里都找得到对应的字段或关系表。

3.2 范式检查:从 1NF 到 3NF,拿选课表现场排查

范式检查是实验报告里最容易被深挖的部分。很多人的表结构拍脑袋建完,答辩时被老师问几个“为什么”就卡壳。我把检查逻辑总结成三步:第一步查 1NF,字段不可再分,这基本不会出问题;第二步查 2NF,表里所有非主属性必须完全依赖主键,不能只依赖主键的一部分;第三步查 3NF,非主属性之间不能有传递依赖。

拿 SC 表举例:(SNO, CNO, GRADE, SEMESTER) 这个关系模式,GRADE 只依赖 SNO 和 CNO 的完整组合,SEMESTER 一样,所以满足 2NF。如果有人在 SC 表里加了 SNAME,那就坏了——SNAME 只依赖 SNO,不依赖 CNO,属于部分依赖,违反 2NF。这在理论上很好理解,但实操中特别容易犯:为了查询方便,把学生的姓名直接塞进选课表,省掉了 JOIN。

再看传递依赖的一个经典案例。学生表里写了 SNO, SNAME, SDEPT, DEPT_ADDR,其中 DEPT_ADDR(系办公地点)依赖于 SDEPT,而 SDEPT 又依赖于 SNO,这就是传递依赖,会把“一个系部地址被修改”变成“要改全系学生记录”,数据冗余和更新异常一起找上门。处理方式是拆成学生表 STUDENT(SNO, SNAME, SDEPT) 和院系表 DEPT(SDEPT, DEPT_ADDR)。我在报告里会专门做一张表,把“不符合范式的关系模式 → 问题类型 → 修改方式”三列写清楚,这比用大篇幅讲理论直观得多,老师扫一眼就知道你掌握了核心概念。

3.3 主键与外键设计:自增编号 vs 业务主键

主键设计是课设里绕不开的争论。不少同学习惯所有表都用一个自增 ID 当主键,理由是简单、不重复。但在数据库系统课设里,我更倾向用业务主键:学生表用学号 SNO,课程表用课程号 CNO,选课表用 (SNO, CNO) 联合主键。原因有两个。

第一,业务主键能直接体现参照完整性。选用自增 ID 时,外键关系变成 STUDENT_ID 引用 STUDENT.ID,你得在报告里额外解释“ID 对应的业务含义”,答辩时容易被追问“你这个 ID 在业务上是什么”。第二,联合主键能天然防止同一学生重复选同一门课,不需要额外写业务判断逻辑,数据库层面就兜住了。代价是代码里写 WHERE 条件时多带一个字段,这对课设来说不算负担。

有些场景确实适合用自增主键,比如日志表、流水号表,它们只有“记录”语义,没有明确的业务标识。如果选题是宿舍报修系统,报修记录表用自增 ID 完全没问题。设计时别一刀切,原则是:有天然唯一业务标识的实体用业务主键,纯粹记录行为的事件表用自增主键。在实验报告里把这条选型依据写清楚,比堆一堆概念强得多。

4. 用 SQL 把库建出来:建表约束、索引、视图、存储过程与触发器,一个都不能少

4.1 建库建表:字符集、数据类型与约束,先想后写

物理设计这一步,你会面临两个流行选择:MySQL 或 SQL Server。南邮这类实验通常不限定数据库产品,我一般推荐 MySQL 8.0,理由是环境好搭、语法资料多、Navicat 或 Workbench 都能可视化操作,调试成本低。

建库建表脚本里,字符集是第一道坑。库、表、字段三层都要显式指定字符集,否则 Windows 下默认可能是 latin1 或 gbk,后续界面传中文数据进来就乱码。我常用的建库语句长这样:

CREATE DATABASE course_select DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE course_select; CREATE TABLE student ( sno CHAR(10) PRIMARY KEY COMMENT '学号', sname VARCHAR(20) NOT NULL COMMENT '姓名', ssex ENUM('男', '女') COMMENT '性别', sage TINYINT UNSIGNED COMMENT '年龄', class_no VARCHAR(12) COMMENT '班级号', sdept VARCHAR(20) COMMENT '院系' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';

SQL 里的注释和字段注释不是写给数据库看的,是写给你自己写实验报告时看的。CHAR(10) 用来存学号,因为学号长度固定且经常做等值查询,定长字符串比 VARCHAR 在这个场景下更合适;SAGE 用 TINYINT UNSIGNED,年龄范围 0~255 足够,没必要给 INT;SEX 用 ENUM 是 MySQL 特色写法,取值固定且能规避脏数据。ENGINE 用 InnoDB,因为它支持外键约束和事务,这两点是实验报告里要重点展示的能力。

4.2 外键与索引:约束要建真实的外键,索引也别乱建

接下来建课程表和选课表。课程表引用教师表,选课表引用学生表和课程表,外键要真实建出来,不要只在逻辑上“觉得有关联”,表结构里却不写 FOREIGN KEY。外键约束是实验报告的要点,也是系统稳定性的保障。索引方面,外键列和常用查询条件列需要建索引,但小表别乱建。

CREATE TABLE course ( cno CHAR(6) PRIMARY KEY COMMENT '课程号', cname VARCHAR(30) NOT NULL COMMENT '课程名', credit DECIMAL(3,1) COMMENT '学分', tno CHAR(8) NOT NULL COMMENT '教师工号', CONSTRAINT fk_course_teacher FOREIGN KEY (tno) REFERENCES teacher(tno) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程表'; CREATE TABLE sc ( sno CHAR(10) NOT NULL COMMENT '学号', cno CHAR(6) NOT NULL COMMENT '课程号', grade DECIMAL(4,1) COMMENT '成绩', semester VARCHAR(11) COMMENT '开课学期', PRIMARY KEY (sno, cno), CONSTRAINT fk_sc_student FOREIGN KEY (sno) REFERENCES student(sno), CONSTRAINT fk_sc_course FOREIGN KEY (cno) REFERENCES course(cno), INDEX idx_sc_cno (cno) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='选课表';

这段建表脚本里有几个细节值得在报告里说明。SC 表的主键是 (SNO, CNO) 联合主键,从数据库层面杜绝了同一个人选同一门课两次。外键约束关键字 CONSTRAINT 给约束起了可读的名字,后续删除外键时能按名字精准操作。最后一个 INDEX 是给 CNO 建的索引,因为按课程统计人数是高频查询。CREDIT 用 DECIMAL(3,1) 是因为学分有 0.5 的精度,DECIMAL 不会被四舍五入成整数,FLOAT 在这个场景不可靠。

4.3 视图:把统计逻辑封装进数据库,报告和演示都靠它加分

视图是实验报告里性价比最高的部分。它的价值不只是“简化查询”,而是展示你理解了“外模式与模式的映象”。我通常在系统里建两个视图,一个给“按课程统计选课人数”,一个给“学生的成绩明细”。视图建好之后,JAVA 端查询代码可以短到一长串 JOIN 都不需要,直接 SELECT 视图名就行。

CREATE VIEW v_course_student_count AS SELECT c.cno, c.cname, COUNT(sc.sno) AS student_count FROM course c LEFT JOIN sc ON c.cno = sc.cno GROUP BY c.cno, c.cname; CREATE VIEW v_student_grade AS SELECT st.sno, st.sname, co.cname, sc.grade, sc.semester FROM student st JOIN sc ON st.sno = sc.sno JOIN course co ON sc.cno = co.cno;

第一个视图用了 LEFT JOIN,这个选择是有讲究的:如果一门课还没有人选,INNER JOIN 会把这门课过滤掉,统计结果就漏了课程。LEFT JOIN 保留课程表的所有行,COUNT 函数对没有选课记录的课程返回 0 而不是 NULL,正好满足“统计每门课选课人数”的需求。第二个视图给教师查询和打印成绩单用,字段已经拼好,程序端直接读。

4.4 存储过程与触发器:实验报告里的加分项,也是翻车重灾区

存储过程用来封装一段带流程的 SQL 逻辑,触发器则是让数据库在 INSERT、UPDATE、DELETE 时自动执行动作。这两样东西在 MIS 里是“数据库独立性”的证明,你写了,报告里就有东西可写;但也是翻车重灾区,语法稍微疏忽就可能跑不出预期效果。

我给选课系统写了一个存储过程,用来按学号查询学生本学期课程和成绩。过程体里需要两条 SELECT,所以用 BEGIN...END 包起来,参数前面加 IN 表示输入参数。写完记得用 CALL 验证一次,并把验证结果截图留作实验报告的过程展示。

DELIMITER $$ CREATE PROCEDURE get_student_courses( IN p_sno CHAR(10) ) BEGIN SELECT c.cname, sc.grade, sc.semester FROM sc JOIN course c ON sc.cno = c.cno WHERE sc.sno = p_sno; END$$ DELIMITER ; CALL get_student_courses('2024010101');

触发器我选的是“选课前校验课程是否满员”,这比单纯统计人数更能体现业务规则。这里用 BEFORE INSERT,NEW 关键字代表即将插入的那一行数据。业务逻辑是:插入选课记录前,统计该课程当前选课人数,如果达到容量上限就抛出错误,阻止插入。

CREATE TRIGGER trg_sc_insert BEFORE INSERT ON sc FOR EACH ROW BEGIN DECLARE v_count INT; DECLARE v_limit INT DEFAULT 50; SELECT COUNT(*) INTO v_count FROM sc WHERE cno = NEW.cno; IF v_count >= v_limit THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '该课程选课人数已满'; END IF; END;

这段触发器有两个关键点。SIGNAL 语句用于主动抛错,SQLSTATE ‘45000’ 表示自定义业务错误,MESSAGE_TEXT 里的中文会原样返回给程序端;用户能在界面上看到“该课程选课人数已满”而不是笼统的数据库异常。NEW 关键字只存在于 INSERT 和 UPDATE 触发器里,写 DELETE 触发器时要用 OLD。需要注意的是,触发器内部默认继承调用者的事务上下文,我在实验报告的说明里会专门写一句:如果事务回滚,触发器抛错导致的插入失败也会一起回滚,这保证了数据一致性。

5. MIS 联调与避坑:前端选型、三层结构与五个经典翻车场景

5.1 前端技术选型:优先选本机跑得动的,别为“高级”牺牲稳定性

做 MIS 的界面层,可选路线很多:JAVA Swing、Java Web(JSP/Servlet)、Python Tkinter、甚至微信小程序。我不建议为了“看起来高级”选一条配置复杂的路线。课设留给联调的时间通常不多,前端稳、跑得动、能演示才是第一优先级。

如果你选 Java 系,Swing 是启动成本最低的方案:JDBC 连数据库,JFrame 拉几个按钮和表格,不需要配置 Tomcat,不会遇到端口占用和乱码之外的 Web 特有坑。JAVA Web 技术栈的好处是更接近生产环境,但要在课设时间内搭 Servlet 容器、配项目结构,时间成本明显更高;如果导师是数据库方向而非 Java EE 方向,界面用 JSP 还是 Swing,评分权重基本一样。Python Tkinter 适合你 Python 熟、Java 生的情况,界面朴素但能把数据库部分讲清楚。整体原则是:把精力留给数据库设计,不要在界面框架上折腾。

5.2 三层结构:界面层、业务层、数据层分开,报告好画图

MIS 哪怕再小,我也建议按三层结构组织代码,这不只是为了代码好看,更是为了让实验报告里能画一张像样的系统架构图。界面层放按钮、表格、输入框;业务层放选课规则、退课规则;数据层放 JDBC 连接和 SQL 操作。哪怕只有三个类,也要把职责划清楚。

JDBC 连接部分有一个每次必踩的细节,就是连接 URL 的参数。以 MySQL 为例,正确的写法是下面这样,参数里显式指定了 UTF-8 编码和时区,没有这两项,中文乱码和日期时区错乱几乎必然发生。

String url = "jdbc:mysql://localhost:3306/course_select" + "?useUnicode=true&characterEncoding=utf8mb4" + "&serverTimezone=Asia/Shanghai&useSSL=false"; String user = "root"; String password = "your_password"; Connection conn = DriverManager.getConnection(url, user, password);

这段代码里 useUnicode=true 和 characterEncoding=utf8mb4 必须成对出现,只写一个不会生效;serverTimezone 是 MySQL 8.0 的硬性要求,不写会报 CST 时区错误;useSSL=false 是本地开发时避免 SSL 握手告警的常用做法。项目里把这套参数统一封装在一个 DBUtil 类里,所有数据访问都走它,后面改密码或者换库名只需要改一处。

5.3 避坑清单:五个经典翻车场景

第一个坑:界面里 insert 的中文全是问号。现象是页面上看到“???”,库里也是乱码。原因往往是三层有一层的编码没对齐,数据库是 utf8mb4,连接串没带 characterEncoding,或者页面编码是 GBK。解决:先统一连接串参数,再确认数据库表字符集是 utf8mb4,最后把 IDE 的文件编码改成 UTF-8,三层统一后问题基本消失。

第二个坑:删除课程时外键约束报错。现象是 DELETE FROM course WHERE cno='C001' 时,数据库提示外键约束失败。原因不是数据库坏了,而是 SC 表里还存在引用这门课的选课记录,外键约束阻止你删掉“有人选过的课”。解决:先删子表记录再删父表记录,或者在事务里先 DELETE FROM sc WHERE cno='C001',再执行 DELETE FROM course。写进报告里时,这正好是外键约束保护数据一致性的正面案例。

第三个坑:程序连不上数据库,报 ClassNotFoundException。现象是运行时报找不到 com.mysql.cj.jdbc.Driver。原因最常见的是忘了把 JDBC 驱动 jar 包放进项目的 lib 目录,或 IntelliJ IDEA 里没把 jar 包 Add as Library。解决:下载对应版本 mysql-connector-j,放进 WEB-INF/lib 或项目 lib 目录并重新构建路径。注意驱动类名:MySQL 5.x 用 com.mysql.jdbc.Driver,8.x 用 com.mysql.cj.jdbc.Driver,混用会报找不到驱动。

第四个坑:触发器“没有生效”。现象是明明往 SC 表里插了第 51 条记录,数据库没有拒绝。原因多半是触发器创建时选错了数据库,或者创建时报了语法错误你没注意到,SHOW TRIGGERS 一查才发现根本没建上。解决:创建完先执行 SHOW TRIGGERS 确认存在,再用一条超限的 INSERT 做验证,把成功的报错信息截图存证,这是实验报告里最硬核的证明。

第五个坑:答辩时报告与系统对不上。现象是报告里 ER 图是 4 个实体,系统里表结构有 6 张表;报告里说触发器限制 50 人,系统里实际能插入 51 条。原因很常见:改代码和改报告不同步。解决:定稿之前重跑一遍完整流程,从建库脚本开始所有 SQL 重新执行,截图全部重拍,实验报告的所有图表必须和最终运行的库完全一致。这一条能帮你避免答辩时最尴尬的场面。

6. 实验报告怎么写:把设计过程讲得像在给团队做评审

实验报告不是把代码贴一遍就完事,它的核心是“设计决策的可追溯性”。我习惯把报告分成六段:系统需求说明、ER 图、关系模式与范式证明、建库 SQL 与关键代码、界面运行截图与测试数据、实验结论与心得体会。每段对应一个评分点,老师按图索骥就能找到他想看的东西。

ER 图用 draw.io 画完之后,导出 PNG 时分辨率选 2x,插进 Word 里才不会发虚。测试数据不要只造“正常数据”,要故意造边界数据:重复选同一门课验证联合主键是否拦截;插入第 51 条选课记录验证触发器;删除有选课记录的课程验证外键约束。每条测试数据后面写清楚“输入是什么、预期结果是什么、实际结果是什么”,这份测试表相当于是给答辩老师的演示脚本。

实验结论部分别写“我学会了数据库设计”这种空话。我会写“系统完成了四次关键验证:联合主键阻止重复选课、外键约束阻止删除被引用课程、触发器限制选课容量、存储过程正确返回成绩明细”,后续接一段“实践过程中对 3NF 的理解从教材概念变成了建模习惯”。把验证结果列清楚,结论自然可信。

我自己的习惯是:动手建表之前,先在纸上把 ER 图改到没有疑问再打开编辑器。课设的坑很多,但绝大多数都能在设计阶段提前排掉。格式是模板给的,思路是自己的。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询