☰
北邮研一数据库大作业:学生成绩管理系统从建表到触发器完整实现
2026/10/3 11:24:08 网站建设 项目流程

简介:这份资源是北邮研一数据库课程大作业的完整详解文档,面向正在学习数据库系统、需要完成课程设计的高校学生。内容围绕学生成绩管理系统展开,涵盖需求分析、数据库设计、ER图绘制、逻辑结构设计与建表程序等环节,可帮助读者理清从需求到实现的完整设计思路。压缩包内共1个docx文件,约503KB,以文字与图表形式呈现设计过程与数据字典。文档详细给出Course、Student、Sc、Teacher四张表的字段定义、主键与外键约束,并说明学生与课程的一对多关系及依赖表Sc的建立方式,同时包含不及格学生名单统计、无教学任务教师查询等功能设计。目前已有606人学习下载,适合需要参考课程设计框架、对照表结构设计与SQL建表语句的读者使用。

1. 北邮研一数据库大作业拆解:学生成绩管理系统从建表到触发器怎么落地

如果你正在搜“北邮研一数据库大作业”,大概率不是想听数据库概论,而是手里有一个必须交的学生成绩管理系统,想知道四张表怎么设计、SQL 怎么写、Navicat 怎么配合、哪些地方容易翻车。这份资源就是围绕这个题目做的完整实现:Course、Student、Sc、Teacher 四张表,覆盖成绩录入、成绩查询、不及格统计、无教学任务教师查询,还带视图、存储过程、触发器、复杂查询和运行环境说明。它适合两类人:一类是刚上手 MySQL、需要照着把作业跑通的研一同学;另一类是想快速判断这份设计能不能直接复用到课程设计里的开发者。下面按“先能建起来、再能查出来、最后能扛住答辩追问”的顺序拆。

2. 四张表怎么建:从 ER 图到 MySQL 建表语句的完整落地

2.1 为什么是 Course、Student、Sc、Teacher 这四张表

这个题目的核心关系不复杂:一个学生可以选多门课,一门课可以被多个学生选,所以 Student 和 Course 之间是多对多。多对多在关系数据库里不能直接落成两张表,必须抽一张联系表,也就是 Sc。Sc 里放 sno、cno、degree,sno 和 cno 做组合主键,同时各自作为外键指回 Student 和 Course。Teacher 和 Course 的关系是“一位教师可以讲多门课,一门课通常由一位教师负责”,所以 Course 表里放 tno 作为任课教师编号,这样“没有教学任务的老师”就能通过 Teacher 左连接 Course 后筛空值查出来。

这里有个容易被忽略的点:题目正文里 Course 表的“选课人数”字段写的是 tno,这其实是笔误,tno 是教师编号,不是人数。真正建表时应该按逻辑结构设计里的 course 表来:cno、cname、tno。如果你照着需求分析那一段把 tno 当人数用,后面查“没有教学任务的老师”时字段含义会直接乱掉。我一般会先以逻辑结构设计为准,再回头检查需求分析里的字段描述是否一致。

2.2 建库建表的可执行 SQL

下面这段可以直接在 MySQL 客户端或 Navicat 查询窗口里跑。注意库名用 test 是原文的写法,实际交作业时建议改成 student_score 这类更语义化的名字,避免和别的库混在一起。

-- 创建数据库,字符集用 utf8mb4 防止中文乱码 CREATE DATABASE test DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE test; -- 课程表:cno 主键,tno 是任课教师编号 CREATE TABLE course ( cno CHAR(5) NOT NULL, cname VARCHAR(20) NOT NULL, tno CHAR(3) NOT NULL, CONSTRAINT C1 PRIMARY KEY (cno) ); -- 学生表:sno 主键,其余字段允许为空 CREATE TABLE student ( sno CHAR(9) PRIMARY KEY, sname CHAR(8), ssex CHAR(2), smajor CHAR(20), sclass CHAR(10) ); -- 成绩表:sno + cno 组合主键,degree 限制 0 到 100 CREATE TABLE sc ( sno CHAR(10) NOT NULL, degree DECIMAL(4,1), cno CHAR(5) NOT NULL, CONSTRAINT A1 PRIMARY KEY (sno, cno), CONSTRAINT A2 CHECK (degree >= 0 AND degree <= 100) ); -- 教师表:tno 主键 CREATE TABLE teacher ( tno CHAR(3) NOT NULL, tname VARCHAR(8), tsex CHAR(2), tdept CHAR(16), CONSTRAINT C2 PRIMARY KEY (tno) );

逻辑说明:建表顺序建议先 course、student、teacher,再 sc,因为 sc 引用了前几张表的主键。参数上,sno 在 student 里是 CHAR(9),在 sc 里是 CHAR(10),原文两处长度不一致,实际插入时如果学号固定 9 位,建议统一成 CHAR(9),否则会出现“同一个人在两个表里长度不同”的别扭情况。degree 用 DECIMAL(4,1) 能存 100.0,CHECK 约束在 MySQL 8.0 之后才真正生效,5.5 版本会解析但可能不强制,这点答辩时如果被问到要能说清楚。

2.3 插入测试数据时怎么避免外键悬空

原文里插入语句用了中文引号,直接跑会报语法错误,必须换成英文单引号。另外原文 teacher 表的插入示例里字段个数和值个数对不上,VALUES 里多了一个“计算机 1403”,而 teacher 表只有 tno、tname、tsex、tdept 四列。这种地方就是典型的“复制粘贴翻车点”。

-- 课程数据 INSERT INTO course VALUES ('C01', '科学导论', '101'); INSERT INTO course VALUES ('C02', '高等数学', '102'); INSERT INTO course VALUES ('C03', '数据结构', '101'); -- 学生数据 INSERT INTO student VALUES ('120210332', '吴迪', '男', '计算机科学与技术', '4412'); INSERT INTO student VALUES ('120210455', '小明', '男', '计算机科学与技术', '4412'); -- 教师数据,注意列数要和值数一致 INSERT INTO teacher VALUES ('101', '叶何斌', '男', '计算机学院'); INSERT INTO teacher VALUES ('102', '张老师', '女', '数学学院'); INSERT INTO teacher VALUES ('103', '李老师', '男', '计算机学院'); -- 成绩数据 INSERT INTO sc VALUES ('120210332', 86.0, 'C01'); INSERT INTO sc VALUES ('120210332', 55.0, 'C03'); INSERT INTO sc VALUES ('120210455', 72.5, 'C02');

逻辑说明:先插 course、student、teacher,再插 sc,是因为 sc 的 sno 和 cno 分别指向 student 和 course。如果顺序反了,在开启外键约束的库上会直接失败。参数上,degree 写 86.0 而不是 '86',能避免隐式类型转换带来的精度问题。测试数据里特意留了一个不及格成绩 55.0,后面统计不及格名单时可以直接验证结果。

3. 视图、存储过程和触发器:把作业从“能跑”拉到“能答辩”

3.1 视图 v_student 和 view_sc 的创建与修改

视图在这个作业里承担两个作用:一是把多表连接封装成简单查询,二是演示“通过视图向基表插入数据”。原文里 v_student 查的是选修“科学导论”的学生学号、姓名和成绩,这个视图适合放在答辩演示里,因为它同时用到了 student、course、sc 三张表。

-- 创建视图:查询选修科学导论的学生成绩 CREATE VIEW v_student AS SELECT A.sno, A.sname, C.degree FROM student A JOIN sc C ON A.sno = C.sno JOIN course B ON B.cno = C.cno WHERE B.cname = '科学导论'; -- 查询视图 SELECT * FROM v_student; -- 创建可更新视图 view_sc CREATE VIEW view_sc AS SELECT sno, degree, cno FROM sc; -- 通过视图插入数据 INSERT INTO view_sc VALUES ('120210455', 88.0, 'C01'); -- 修改视图定义 ALTER VIEW view_sc AS SELECT sno, degree, cno FROM sc WHERE degree IS NOT NULL; -- 删除视图 DROP VIEW view_sc;

逻辑说明:v_student 用了 JOIN 而不是逗号连接,可读性更好,也不容易漏掉连接条件。view_sc 之所以能插入,是因为它只来自单表 sc,且没有聚合、去重、分组,属于可更新视图。如果视图里带了 AVG、GROUP BY 或者 UNION,插入就会失败。参数上,ALTER VIEW 改的是视图定义,不是数据;DROP VIEW 只删视图,不影响 sc 基表里的数据,这两个点答辩时经常被追问。

3.2 存储过程 proc_stud 和 num_sc 的参数设计

存储过程是这份作业里比较能体现“数据库编程”的部分。原文给了两个:一个按班级查学生,一个统计某个学生的选课门数。第二个用了 IN 和 OUT 参数,是典型的“输入学号、输出数量”模式。

DELIMITER // -- 查询班级包含 4412 的学生 CREATE PROCEDURE proc_stud() READS SQL DATA BEGIN SELECT sno, sname, smajor FROM student WHERE sclass LIKE '%4412%' ORDER BY sno; END // -- 统计指定学号的课程成绩个数 CREATE PROCEDURE num_sc(IN tmp_sno CHAR(9), OUT count_num INT) READS SQL DATA BEGIN SELECT COUNT(*) INTO count_num FROM sc WHERE sno = tmp_sno; END // DELIMITER ; -- 调用无参存储过程 CALL proc_stud(); -- 调用带 OUT 参数的存储过程 CALL num_sc('120210332', @cnt); SELECT @cnt;

逻辑说明:DELIMITER // 是为了让 MySQL 把存储过程内部的封号当成普通语句分隔符,最后再恢复成封号。参数上,IN 表示传入,OUT 表示传出,调用时用用户变量 @cnt 接收。READS SQL DATA 表示过程只读数据,不改数据,这个声明在权限管理和答辩时都能加分。注意 tmp_sno 的长度最好和 student.sno 保持一致,原文写 CHAR(9),但 sc.sno 是 CHAR(10),如果学号实际是 9 位,建议统一。

3.3 触发器 trig_student 和 delstudent 备份表

触发器的需求是:删除 student 里的学生时,把学号和姓名写进 delstudent 表。这个设计本质上是一个简易审计日志,答辩时可以说成“保留删除痕迹,便于追溯”。

-- 创建空备份表,结构来自 student CREATE TABLE delstudent AS SELECT sno, sname FROM student WHERE 1 = 0; -- 创建删除触发器 CREATE TRIGGER trig_student AFTER DELETE ON student FOR EACH ROW INSERT INTO delstudent(sno, sname) VALUES (OLD.sno, OLD.sname); -- 验证:删除一个学生 DELETE FROM student WHERE sname = '李甜甜'; -- 查看备份表 SELECT * FROM delstudent;

逻辑说明:CREATE TABLE ... AS SELECT ... WHERE 1=0 是常见的“只复制结构不复制数据”写法。触发器用 AFTER DELETE,因为只有删除成功后才需要记录。OLD.sno 和 OLD.sname 代表被删除行原来的值,MySQL 里删除操作只能用 OLD,不能用 NEW。参数上,FOR EACH ROW 表示行级触发器,删一行触发一次。如果一次删多行,delstudent 里会插入多条记录。

4. 高频查询 SQL:不及格名单、无教学任务教师和平均分怎么一次写对

4.1 统计不及格学生名单的多表连接写法

教务处要查各科不及格学生名单,这个查询要同时拿到学生信息、课程信息和成绩。原文用的是 INNER JOIN,这是对的,因为不及格记录一定同时存在于 sc 和 student 中。

-- 查询 C03 课程不及格的学生信息 SELECT A.sno, A.sname, A.ssex, A.smajor, A.sclass, B.degree FROM student A INNER JOIN sc B ON A.sno = B.sno INNER JOIN course C ON B.cno = C.cno WHERE C.cno = 'C03' AND B.degree < 60;

逻辑说明:连接顺序是 student 到 sc 到 course,先通过 sno 关联学生和成绩,再通过 cno 关联课程。WHERE 里同时限制课程号和分数,能精确到“某一门课的不及格名单”。参数上,degree < 60 是不及格线,如果学校有 60 分以下和 60 分整的区别,边界要确认清楚。如果要把所有课程的不及格名单都列出来,去掉 C.cno = 'C03' 即可,但建议加上 ORDER BY C.cno, A.sno,方便阅读。

4.2 查询没有教学任务的老师名单

这个需求是整份作业里最容易写错的。核心思路是:从 teacher 表出发,左连接 course 表,然后筛出 course 侧为 NULL 的记录。因为如果一位老师没有课,左连接后 course 的字段全是 NULL。

-- 查询没有教学任务的教师 SELECT T.tno, T.tname, T.tdept FROM teacher T LEFT JOIN course C ON T.tno = C.tno WHERE C.cno IS NULL;

逻辑说明:LEFT JOIN 保证 teacher 表所有行都保留,course 表匹配不上的行在 C.cno 上显示 NULL。WHERE C.cno IS NULL 就是筛出这些没匹配上的老师。参数上,判断 NULL 必须用 IS NULL,不能用 = NULL,这是 SQL 里最常见的坑之一。如果 course 表里 tno 允许为空,还要考虑“课程存在但没分配老师”的情况,那种场景下应该用 NOT EXISTS 或 NOT IN,但本题的 course.tno 是 NOT NULL,所以左连接方案足够。

4.3 平均成绩和条件查询的边界

计算某门课平均分、查选修某课的学生、插入新学生,这些都属于基础操作,但有几个边界要注意。

-- 计算 C01 课程平均成绩 SELECT AVG(degree) AS avg_degree FROM sc WHERE cno = 'C01'; -- 查询选修高等数学的学生学号和姓名 SELECT A.sno, A.sname FROM student A INNER JOIN sc B ON A.sno = B.sno INNER JOIN course C ON B.cno = C.cno WHERE C.cname = '高等数学'; -- 插入新学生,只填必填字段 INSERT INTO student (sno, sname, ssex) VALUES ('120210455', '小明', '男');

逻辑说明:AVG 会自动忽略 NULL,如果某学生 degree 为空,不会拉低平均分,但也不会被计入。参数上,插入语句只写部分列时,其余列取默认值或 NULL,前提是这些列允许为空。如果 sno 已经存在,会报主键冲突,这是正常的,说明约束在起作用。

5. 避坑与排查:这份大作业最容易翻车的五个地方

5.1 中文引号导致 SQL 语法错误

现象:把原文里的 INSERT 语句复制到 Navicat 里执行,报 “You have an error in your SQL syntax”。原因:原文用的是中文全角引号 ‘ ’ 和 “ ”,MySQL 只认英文半角单引号。解决:把所有值两边的引号替换成英文单引号,建表语句里的注释 // 也要改成 -- 或 /* */,否则同样会报错。

5.2 字段长度不一致导致关联查不出数据

现象:student.sno 是 CHAR(9),sc.sno 是 CHAR(10),插入时看起来都有值,但 JOIN 之后查不到记录。原因:CHAR 类型在比较时会补空格,长度不一致时可能影响等值匹配,尤其是数据里混有前后空格。解决:统一 sno 长度为 CHAR(9),插入前用 TRIM() 清理空格,或者建表时就按同一标准定义。

5.3 CHECK 约束在 MySQL 5.5 不生效

现象:degree 插入 -10 或 200,表里居然存进去了。原因:MySQL 5.5 及更早版本会解析 CHECK 但不会强制执行,只有 8.0.16 之后才真正生效。解决:如果环境是 5.5,成绩范围要在应用层或触发器里校验;如果可以用 8.0,保留 CHECK 并说明版本要求。答辩时被问到“你的约束真的生效了吗”,要能答出这个版本差异。

5.4 触发器删除后备份表没数据

现象:执行 DELETE 后,delstudent 表是空的。原因:可能是触发器创建失败但没注意报错,或者删除条件没匹配到任何行,也可能是 AFTER DELETE 写成了 BEFORE DELETE 但逻辑不对。解决:先 SHOW TRIGGERS; 确认触发器存在,再用 SELECT * FROM student WHERE sname='李甜甜'; 确认有数据,最后检查 OLD.sno 和 OLD.sname 是否写对。

5.5 Navicat 图形化操作和命令行结果不一致

现象:在 Navicat 里手动改了一条数据,命令行查询结果和预期不同。原因:Navicat 可能没有自动提交事务,或者改的是视图而不是基表。解决:在 Navicat 里确认当前连接的是 test 库,修改后点“提交”;命令行里用 SELECT * FROM sc; 直接验证基表。涉及视图更新时,先确认视图是否可更新。

6. 进阶技巧:用 EXPLAIN 和索引把查询从“能跑”调到“能看”

这份作业的查询数据量不大,但答辩时老师很可能问一句“如果数据量大了怎么办”。这时候不要空谈优化,直接上 EXPLAIN 看执行计划,再决定加不加索引。我一般会先对 sc 表的 sno 和 cno 分别建索引,因为这两个字段在连接和条件过滤里出现频率最高。

-- 查看不及格查询的执行计划 EXPLAIN SELECT A.sno, A.sname, B.degree FROM student A INNER JOIN sc B ON A.sno = B.sno WHERE B.degree < 60; -- 在 sc 表上建索引 CREATE INDEX idx_sc_sno ON sc(sno); CREATE INDEX idx_sc_cno ON sc(cno); CREATE INDEX idx_sc_degree ON sc(degree); -- 再次查看执行计划,对比 type 和 rows EXPLAIN SELECT A.sno, A.sname, B.degree FROM student A INNER JOIN sc B ON A.sno = B.sno WHERE B.degree < 60;

逻辑说明:EXPLAIN 输出里重点看 type、key 和 rows。type 从 ALL 变成 ref 或 range,说明索引生效;rows 变小,说明扫描行数减少。参数上,索引不是越多越好,sc 表本身是组合主键 (sno, cno),已经能覆盖按 sno 或 sno+cno 的查询,单独再建 idx_sc_sno 可能冗余,但按 cno 单独查时 idx_sc_cno 有用。degree 上的索引在低选择性列上效果有限,如果不及格人数占比很高,优化器可能仍然走全表扫描,这是正常的。

还有一个容易被忽略的点:存储过程和触发器在答辩演示时最好准备“失败案例”。比如故意插入一个重复学号,展示主键冲突;故意删除一个不存在的学生,展示触发器不触发。这样比只演示成功路径更有说服力。我每次交这类作业前,都会把建表、插数据、视图、存储过程、触发器、复杂查询按顺序完整跑一遍,把报错信息也截图留着,因为那些报错往往就是答辩时被追问的地方。从那以后我每次做数据库作业都强制走一遍“删库重建”流程,确保脚本从头到尾可复现。希望帮到你。

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

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

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

立即咨询