我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

数据库课程设计实战:学生成绩管理系统四表设计与SQL实现

数据库课程设计实战:学生成绩管理系统四表设计与SQL实现 简介这份资源是北邮研一数据库课程大作业的完整详解文档面向计算机相关专业研究生及需要完成数据库课程设计的学习者围绕学生成绩管理系统的设计与实现展开。内容从需求分析入手梳理成绩维护、教师信息管理、不及格学生名单统计及无教学任务教师查询等功能并给出Course、Student、Sc、Teacher四张表的数据字典、主外键约束与一对多关系设计同时包含局部与全局ER图、逻辑结构设计及建表SQL语句可帮助读者理清从概念模型到关系模式的完整推导过程。资源包共1个docx文件约503KB以图文与代码结合的方式呈现便于对照复现建库建表流程。目前已有607人学习适合作为数据库大作业的参考范本与查漏补缺材料。1. 学生成绩管理系统从教务科三句话需求到可验收的数据库方案教务科提需求往往就三句话老师能录成绩、学生能查成绩、教务处能拉出不及格名单和没课的老师名单。听着简单真动手做数据库大作业时很多人第一版就翻车——表建完了发现「没有教学任务的老师」这个查询写不出来因为课程表里塞的是教师号教师和课程的关系没拆干净。这篇就按一份完整的数据库课程设计来拆需求怎么落到四张表、ER 图怎么画才不返工、SQL 怎么写才经得起答辩追问、最后怎么自测验收。适合正在做数据库大作业、课程设计或者要交一份能跑能讲的关系型数据库方案的人直接照着改。2. 需求与数据建模四张表怎么撑起三类角色的查询2.1 先把三类角色的动作翻译成数据操作需求文档里写的是「维护和管理」「按条件查询」这种话不能直接进设计。我一般会把每个角色拆成具体动作再对应到表和字段角色动作落到哪张表关键字段授课教师录入、修改成绩scsno、cno、degree学生查询本人成绩sc 关联 coursesno、cname、degree教务处统计某课不及格名单sc 关联 studentcno、degree60教务处查无教学任务的教师teacher 左连接 coursetno这张表看着朴素但它决定了后面所有设计。比如「无教学任务教师」这条本质是 teacher 表里存在、但 course 表里没有对应 tno 的记录属于典型的反连接查询。如果建表时把教师信息冗余进 course 表这条查询就得靠字符串比对既不规范也容易出错。所以四张表的分工必须在需求阶段就定死teacher 存人course 存课student 存学生sc 存「谁选了什么课考了多少分」这个多对多关系。2.2 四张表的字段与约束设计按项目给出的数据字典四张表的结构如下这里把类型、长度、可空性和约束一次列清建表时直接照抄表字段类型长度可空约束coursecnochar5否主键coursecnamevarchar20否—coursetnochar3否关联 teacherstudentsnochar9否主键studentsnamechar8是—studentssexchar2是男/女studentsmajorchar20是—studentsclasschar10是—scsnochar10否组合主键、外键sccnochar5否组合主键、外键scdegreedecimal4,1是0–100teachertnochar3否主键teachertnamevarchar8是—teachertsexchar2是男/女teachertdeptchar16是—有两处细节值得在答辩时主动讲一是 sc 表的 degree 用 decimal(4,1)能存 100.0 也能存 0.0配合 CHECK 约束把范围卡死在 0 到 100比用 int 更贴合成绩场景二是 sc 的主键是 (sno, cno) 组合主键这直接保证了「一个学生对一门课只有一条成绩记录」重复录入在数据库层面就被挡住了不用靠应用层去查重。2.3 ER 图与关系模式的对应关系局部 ER 图里学生和课程之间是 m:n 的选修关系教师和课程之间是 1:n 的讲授关系。转成关系模式时m:n 必须独立成表这就是 sc 的由来1:n 则可以把「一」端的主键放到「多」端做外键所以 course 表里带 tno。全局 ER 图合并后得到四个关系模式course(cno, cname, tno)student(sno, sname, ssex, smajor, sclass)sc(sno, cno, degree)teacher(tno, tname, tsex, tdept)四个模式的主属性都是候选码非主属性完全函数依赖于码满足第三范式。这一点在报告里要写清楚因为答辩老师十有八九会问「你的表到 3NF 了吗凭什么」。判断方法很直接每个非主属性都由整个主键决定、且不传递依赖其他非主属性逐表过一遍即可。3. 建库建表与约束落地把设计图变成能跑的 DDL3.1 建库与四张表的完整 DDL设计讲完就得落地。下面这段 DDL 按项目里的建表语句整理补上了外键约束和字段注释直接在一个空 MySQL 实例上执行就能得到完整结构-- 创建数据库字符集显式指定避免中文姓名乱码 CREATE DATABASE score_mgmt DEFAULT CHARACTER SET utf8mb4; USE score_mgmt; -- 教师表先建因为 course 要引用它 CREATE TABLE teacher ( tno CHAR(3) NOT NULL COMMENT 教师编号, tname VARCHAR(8) NULL COMMENT 教师姓名, tsex CHAR(2) NULL COMMENT 性别男/女, tdept CHAR(16) NULL COMMENT 所属院系, CONSTRAINT pk_teacher PRIMARY KEY (tno), CONSTRAINT ck_tsex CHECK (tsex IN (男, 女)) ); -- 课程表tno 外键指向 teacher CREATE TABLE course ( cno CHAR(5) NOT NULL COMMENT 课程编号, cname VARCHAR(20) NOT NULL COMMENT 课程名称, tno CHAR(3) NOT NULL COMMENT 任课教师编号, CONSTRAINT pk_course PRIMARY KEY (cno), CONSTRAINT fk_course_teacher FOREIGN KEY (tno) REFERENCES teacher(tno) ); -- 学生表 CREATE TABLE student ( sno CHAR(9) NOT NULL COMMENT 学号, sname CHAR(8) NULL COMMENT 姓名, ssex CHAR(2) NULL COMMENT 性别男/女, smajor CHAR(20) NULL COMMENT 专业, sclass CHAR(10) NULL COMMENT 班级, CONSTRAINT pk_student PRIMARY KEY (sno), CONSTRAINT ck_ssex CHECK (ssex IN (男, 女)) ); -- 成绩表组合主键 双外键 分数范围约束 CREATE TABLE sc ( sno CHAR(10) NOT NULL COMMENT 学号, cno CHAR(5) NOT NULL COMMENT 课程编号, degree DECIMAL(4,1) NULL COMMENT 成绩0-100, CONSTRAINT pk_sc PRIMARY KEY (sno, cno), CONSTRAINT fk_sc_student FOREIGN KEY (sno) REFERENCES student(sno), CONSTRAINT fk_sc_course FOREIGN KEY (cno) REFERENCES course(cno), CONSTRAINT ck_degree CHECK (degree 0 AND degree 100) );建表顺序不能乱teacher 和 student 是「被引用方」必须先建course 引用 teachersc 同时引用 student 和 course所以顺序是 teacher → student → course → sc。如果反过来先建 sc外键会直接报错。另外 CHECK 约束在部分 MySQL 版本里会被静默忽略答辩前务必用一条越界插入验证它到底生效没有别在演示时被老师一句话问穿。3.2 索引设计哪些查询值得加索引索引不是越多越好写多了拖慢插入、占空间写少了查询全表扫。按这个系统的实际查询模式我一般只加三类-- 按课程统计成绩、查不及格名单cno 是高频过滤条件 CREATE INDEX idx_sc_cno ON sc(cno); -- 按姓名模糊查学生前缀匹配能用上索引 CREATE INDEX idx_student_sname ON student(sname); -- 按班级查学生存储过程里用到 CREATE INDEX idx_student_sclass ON student(sclass);sno 在 sc 里已经是组合主键的左前缀单独再建 sno 索引属于重复没必要。degree 这种低选择性的字段也不适合单独建索引查不及格名单时走 cno 索引再过滤 degree 更划算。这套取舍在报告里写一句「索引围绕高频等值查询和连接字段建立」比堆一堆索引更有说服力。3.3 视图把常用联表查询封装起来学生查成绩要联三张表每次都写一遍容易出错用视图封装是标准做法。项目里定义了 v_student 视图查某门课的选课学生成绩这里再补一个更通用的学生成绩视图-- 学生-课程-成绩三表联查视图学生端查询直接走它 CREATE VIEW v_score_detail AS SELECT s.sno, s.sname, c.cno, c.cname, sc.degree FROM student s JOIN sc ON s.sno sc.sno JOIN course c ON sc.cno c.cno; -- 学生查自己成绩 SELECT * FROM v_score_detail WHERE sno 120210332;视图的好处不只是少写 SQL更重要的是把「成绩 学生表 成绩表 课程表」这个业务口径固定下来前端或报表都从同一个视图取数不会出现两个地方算法不一致的玄学问题。注意视图默认不可更新涉及多表连接的视图做 INSERT/UPDATE 会失败录入成绩还是要直接操作 sc 表。4. 业务功能实现存储过程、触发器与核心查询4.1 用存储过程封装统计逻辑教务处要「按班级查学生」和「统计某学生选了几门课」这类固定逻辑适合做成存储过程调用方传参就行不用每次拼 SQLDELIMITER // -- 按班级查学生入参为班级关键字 CREATE PROCEDURE proc_stud_by_class(IN p_class VARCHAR(10)) READS SQL DATA BEGIN SELECT sno, sname, smajor FROM student WHERE sclass LIKE CONCAT(%, p_class, %) ORDER BY sno; END // -- 统计某学生的选课门数OUT 参数返回结果 CREATE PROCEDURE num_sc(IN p_sno CHAR(9), OUT p_count INT) READS SQL DATA BEGIN SELECT COUNT(*) INTO p_count FROM sc WHERE sno p_sno; END // DELIMITER ;调用方式CALL proc_stud_by_class(4412);和CALL num_sc(120210332, cnt); SELECT cnt;。这里有两个容易踩的点一是 DELIMITER 必须改否则存储过程体内的分号会被客户端当成语句结束二是声明了 READS SQL DATA 表示只读能减少不必要的锁答辩时被问到「为什么加这个」能答上来是加分项。4.2 触发器删除学生时留一份归档学生被删除后成绩记录跟着没了教务追溯就断了。项目里用触发器把删除的学生学号姓名写进归档表这个思路很实用-- 归档表结构与 student 的关键字段一致 CREATE TABLE delstudent ( sno CHAR(9), sname CHAR(8), del_time DATETIME DEFAULT CURRENT_TIMESTAMP ); DELIMITER // CREATE TRIGGER trig_student_del AFTER DELETE ON student FOR EACH ROW BEGIN INSERT INTO delstudent(sno, sname) VALUES (OLD.sno, OLD.sname); END // DELIMITER ;注意触发器是 AFTER DELETE用 OLD 取被删行的值。这里有个血泪经验如果 sc 表对 student 有外键且没设级联删除直接删学生会被外键挡住触发器根本不会触发。所以要么先删 sc 里的成绩要么把外键设成 ON DELETE CASCADE。我一般选前者删除操作走一个事务先删成绩再删学生归档和清理一起提交逻辑更可控。4.3 三类核心查询的写法与验证需求里点名的几个查询写法要经得起推敲-- 1. 某课程不及格学生名单教务处高频 SELECT s.sno, s.sname, s.sclass, sc.degree FROM sc JOIN student s ON sc.sno s.sno WHERE sc.cno C03 AND sc.degree 60 ORDER BY sc.degree; -- 2. 没有教学任务的教师反连接用 LEFT JOIN 判空 SELECT t.tno, t.tname, t.tdept FROM teacher t LEFT JOIN course c ON t.tno c.tno WHERE c.tno IS NULL; -- 3. 某课程平均分与选课人数 SELECT c.cname, COUNT(*) AS stu_cnt, AVG(sc.degree) AS avg_score FROM sc JOIN course c ON sc.cno c.cno WHERE sc.cno C01 GROUP BY c.cno, c.cname;第二条是整套方案里最值得讲的一条。用 NOT IN 也能写但子查询里一旦出现 NULLNOT IN 会返回空结果集这是 SQL 里经典的坑。LEFT JOIN IS NULL 的写法语义清晰、执行计划稳定答辩时能主动说出「为什么不用 NOT IN」基本就把这道题拿下了。5. 避坑与排查数据库大作业最容易翻车的五个点5.1 中文乱码建库没指定字符集现象是插入「张三」查出来变成问号或乱码。原因通常是数据库、表、连接三处字符集不一致默认 latin1 在作怪。解决办法是建库时显式DEFAULT CHARACTER SET utf8mb4建表继承库的字符集连接串里也带上characterEncodingutf8。已经建好的库可以用ALTER DATABASE ... CHARACTER SET utf8mb4补救但已有数据要重新导入才彻底。5.2 外键报错 1452插入顺序或数据缺失现象是往 sc 插成绩时报「Cannot add or update a child row」。原因是 sc.sno 或 sc.cno 在父表里不存在。排查顺序先查 student 和 course 里有没有这条记录再确认插入语句的字段顺序和类型是否对得上char 类型带不带引号差别很大。解决就是先插父表数据或者临时SET FOREIGN_KEY_CHECKS0导入完再打开——但后者只适合批量初始化日常操作别关。5.3 CHECK 约束不生效版本差异现象是插入 degree 120 居然成功了。原因是部分 MySQL 版本解析 CHECK 但不执行。解决办法有两个升级到支持 CHECK 的版本或者在应用层/触发器里补一道校验。答辩演示前一定用越界数据实测一次别默认它生效。5.4 触发器不触发外键挡住了删除现象是执行 DELETE 后归档表没数据。原因是 sc 表外键约束阻止了删除语句直接报错回滚。解决是把删除包装成事务先删 sc 再删 student或者给外键加 ON DELETE CASCADE。排查时先单独跑 DELETE 看报什么错错误信息会直接指向外键。5.5 存储过程创建失败分隔符没改现象是 CREATE PROCEDURE 报语法错误指向过程体里的分号。原因是客户端把过程体里的分号当成了语句结束。解决就是前后加DELIMITER //和DELIMITER ;并且确认客户端工具支持这一指令。Navicat 里在查询窗口执行同样有效但要注意别在已经改了分隔符的状态下继续执行普通语句。6. 验收与答辩怎么证明这套库真的能用验收不是把 SQL 跑一遍给老师看而是按需求逐条对账。我一般准备一张自测清单每条需求对应一条可执行的验证语句和预期结果现场按顺序演示验收项验证操作预期结果成绩录入向 sc 插入一条合法成绩插入成功视图可查成绩范围约束插入 degree120被 CHECK 拒绝重复选课约束重复插入同一 (sno,cno)主键冲突报错不及格名单执行 4.3 第一条查询只返回 degree60 的记录无课教师执行 4.3 第二条查询返回 course 中无记录的教师删除归档删除一名学生后查 delstudent归档表出现该学生存储过程CALL num_sc 传入学号返回正确门数演示顺序也有讲究先跑「正常路径」建立信心再跑「约束拦截」展示设计严谨性最后跑触发器和存储过程展示进阶能力。老师追问最多的地方通常是范式判断和外键必要性提前把「为什么 sc 要组合主键」「为什么 course 的 tno 不做成多值字段」这两个问题的答案背熟基本不会卡壳。还有一个容易被忽略的点报告里的 ER 图和实际建表必须对得上。我见过图里画了「授课」联系、表里却没有对应字段的情况老师一眼就能看出来。交之前拿建表语句反推一遍关系模式和图逐条比对比事后解释省事得多。从那以后我每次交数据库方案前都会强制走一遍「需求条目 → 表字段 → 验证语句」的三列对账任何一条需求找不到对应的验证 SQL就说明设计有缺口。这套习惯帮我躲过了好几次答辩现场的尴尬。希望帮到你。本文还有配套的精品资源点击获取
返回列表