☰
学生选课系统数据库设计实战:从E-R图到SQL Server建表与备份还原
2026/10/13 8:39:41 网站建设 项目流程

简介:一份围绕《学生选课信息管理系统的设计与实现》的数据库课程设计PDF资料,适合软件工程、计算机等专业学生完成基于SQL Server和VB的C/S模式选课系统课设时参考。文档完整呈现上海电机学院课程设计范本,按正式报告顺序编排,依次包含封面、课程设计评分表、答辩记录表、摘要目录、系统概述、需求分析、需求描述、数据流图、数据字典、E-R图、实体及属性定义、逻辑结构设计、规范化处理、物理结构设计、SQL Server数据表设计、索引表、角色、视图和数据库插入操作等模块。内容不只给出最终方案,还展示了从需求分析、数据库设计到系统实现的完整过程,便于对照练习。资源包共1个PDF文件,大小1.25MB,文档内容完整、层次清晰。已有277人学习,读者可对照它撰写课程设计报告,理解数据库概念设计、逻辑设计与物理设计的完整路径,并用于答辩准备。

1. 学生选课信息管理系统:一份十年前课设文档的含金量盘点

这份 PDF 你得跳着看:数据库部分值得反复啃,VB 界面部分只能当地图用。它是上海电机学院 2011 年的一份数据库原理课程设计,技术栈是 SQL Server 后台 + VB 前台,C/S 架构——既不是 B/S,也不是现在流行的前后端分离,但恰恰因为技术老,反而把数据库设计这条主线暴露得很干净。全文覆盖了需求分析、数据流图、数据字典、E-R 图、3NF 规范化、建表 SQL、索引、视图、角色、备份还原和界面菜单规划,是典型的「文档完整性」课设模板。适合三类人:准备交数据库课程设计的学生、想快速搭学生选课原型的人、以及带项目的初级工程师——你可以直接借它的表结构和核心 SQL,省掉从零建模的功夫。

2. 需求分析与建模:从数据流图到 3NF 关系模式的完整推导

2.1 需求描述:先圈定系统边界

文档把系统功能划成五块:学生管理(查询、插入、删除、修改)、课程管理(同上四件套)、选课管理(同上四件套)、系统管理(操作员信息管理)、系统维护(数据安全管理,含备份与恢复、操作员管理、权限设置)。这个划分很典型,几乎可以照搬进任何课设的需求分析章节。

但注意一个细节:数据流图里出现过教师角色(教师发布课程信息),而实体设计和 E-R 图里并没有教师表。如果你照抄这套文档,后续想加「教师端」就得为教师单独建模、加发布课程的权限;不加的话,当前系统里教师只是一个外部数据源,不影响主流程。这一点在答辩时经常被老师追问,提前想好说法。

2.2 数据流图:从四张图读出系统校验逻辑

文档里的数据流图是简化版,共四张:第一张是全局图「信息规范管理系统」,后三张分别对应查询、插入、修改删除的处理流程。读数据流图的关键不是看图形画得多标准,而是看数据从哪来、经过什么处理、落到哪个存储。

这里最有价值的信息是:操作员输入的数据要先经过「规范表」核对,变成「正确信息」后才能进入更新结果。这意味着系统必须在写入前做合法性校验——性别只能男或女、学号不能重复、课程号必须存在。这些约束在后文的 CHECK 约束、PRIMARY KEY 和外键引用里都会一一对应上。数据流图画到这个粒度对课设打分已经够用;真实开发里更推荐用用例图加状态图补充,数据流图本身更适合做系统边界确认。

2.3 数据字典:字段先定死,后面才不返工

文档给出了完整的数据字典,汇总如下:

数据元素数据类型长度/精度所在表
学号 Snochar5学生表
姓名 Snamechar8学生表
性别 Sexchar2学生表
出生日期 Sbirthsmalldatetime默认学生表
所在系 Sdeptchar20学生表
课程代码 Cnochar3课程表
课程名称 Cnamechar20课程表
学分 Creditdecimal3,1课程表
成绩 Gradedecimal5,1选课表
操作员编号 Operator_idchar4操作员表
操作员姓名 Operator_namechar12操作员表
密码 Passwordchar10操作员表
权限 Permissionchar20操作员表

数据字典最容易被新手跳过,但它是后续 E-R 图和建表 SQL 能否自洽的根基。比如 Course 表的 Credit 用 decimal(3,1),支持 0.5 学分;SC 表的 Grade 用 decimal(5,1),能存 99.9,百分制够用。另一个值得注意的点是:主码长度决定了外键引用的匹配——Sno char(5) 引用到 SC 表时,两边长度必须一致,否则 SQL Server 会报类型或长度不匹配的错。

2.4 E-R 图与关系模式:四张表是这样导出来的

E-R 图里有三个实体:学生、课程、操作员。学生和课程之间是 m:n 关系(一个学生选多门课,一门课被多个学生选)。操作员实体在 E-R 图里没有与任何实体连线——这是一个小瑕疵,但不影响后续实现,因为它单独成表、单独管理。

m:n 关系转关系模式的规则是:取两端实体的主码,加上关系自身的属性(成绩 Grade),组成新的选课表 SC,复合主码为 (Sno, Cno)。文档给出的最终关系模式如下:

  • Student(Sno, Sname, Sex, Sbirth, Sdept)
  • Course(Cno, Cname, Credit)
  • SC(Sno, Cno, Grade)
  • Operator(Operator_id, Operator_name, Password, Permission)

下划线代表主码,斜体代表外码。实际操作时,这一步我用一个笨办法校验:把 E-R 图里的每个实体和关系都写成一行,然后数主码和外码有没有漏——漏一个,后面建表时外键引用就会断。

2.5 规范化:为什么说它已经到 3NF

文档里写「不存在部分函数依赖和传递函数依赖,已经达到 3NF」,这个判断是对的。具体拆开看:SC 表的复合主码是 (Sno, Cno),Grade 完全函数依赖于整个主码,不单独依赖 Sno 或 Cno,所以满足 2NF;同时 SC 表里没有非主属性依赖另一个非主属性,所以没有传递依赖,达到 3NF。Student 表里如果放了「系主任」这类依赖 Sdept 的属性,就会破坏 3NF——但文档里没放,所以干净。

这里多说一句:答辩时老师很可能追问「为什么不是 BCNF」。可以从函数依赖集的角度回答——每张表的每个决定因素都是候选码,因此就是 BCNF。如果回答不上来,至少把「3NF 消除了部分依赖和传递依赖」这句话吃透。

3. 数据库物理设计:建表 SQL、视图与角色权限的参数取舍

3.1 建表 SQL:四张表一次跑通

文档给出的建表 SQL 可以直接复制到 SQL Server Management Studio 执行,我整理时把 PDF 里被截断的 SQL 补全,并加上了必要的注释:

create table Student( Sno char(5) primary key, Sname char(8) not null, Sex char(2) check (Sex in ('男','女')), Sbirth smalldatetime, Sdept char(20) ); go create table Course( Cno char(3) primary key, Cname char(20) not null, Credit decimal(3,1) ); go create table SC( Sno char(5) references Student(Sno), Cno char(3) references Course(Cno), Grade decimal(5,1), constraint pk primary key(Sno, Cno) ); go create table Operator( Operator_id char(4) primary key, Operator_name char(12) not null, Password char(10), Permission char(20) default '0' ); go

逻辑说明:Student 表的 Sno 设为主键,Sex 加了 CHECK 约束只允许男/女;SC 表同时建了复合主键 pk 和两个外键引用,意味着插入选课记录时 Sno 和 Cno 必须分别在 Student 和 Course 中存在,从数据库层面杜绝「选了不存在的课」这类脏数据。

参数说明几个关键点:Credit decimal(3,1) 表示最多 2 位整数加 1 位小数,能存最大值 99.9;Grade decimal(5,1) 是 4 位整数加 1 位小数,百分制成绩绰绰有余。Password 用 char(10) 且不加密,这在课设里能过,但放到真实系统必须换哈希存储,比如 SHA-256 加盐。Permission default '0' 表示默认权限为最低级别,这个设计思路不错——默认拒绝,按需开放。

3.2 索引、视图与角色:三件套的常见配置

文档里索引相关的「索引表」其实只是列清单,真正创建索引用的是 CREATE INDEX 语句。以学生选课系统的查询场景为例,高频查询是按成绩排序和按系查学生,所以常见做法是给 Grade 和 Sdept 建非聚集索引:

create index idx_sc_grade on SC(Grade); create index idx_student_sdept on Student(Sdept);

索引不是越多越好——每个索引都会拖慢 INSERT/UPDATE 的性能,因为写入时索引也要同步更新。对课设系统来说,两张表的两个非聚集索引足够;生产环境则需要根据慢查询日志持续调整。

视图部分,文档给了两条建立路径:一条是用 SSMS 的创建视图向导,另一条是 SQL 语句。文档里的 SQL 语句明显被 PDF 截断过,我按标准语法补全如下:

create view 学生_成绩 as select Student.Sno, Student.Sname, Course.Cname, SC.Grade from Student join SC on Student.Sno = SC.Sno join Course on Course.Cno = SC.Cno;

这条视图把学生、课程、成绩三张表 join 在一起,对外暴露的是「学号、姓名、课程名、成绩」四个字段。实际应用中我一般会用英文视图名(比如 v_student_score),原因后面避坑章有说明——中文标识符在 ADO/ODBC 链路里会出幺蛾子。

角色部分,文档只写了一句话「建立了操作员角色」,没有给出具体 SQL。补全的话,SQL Server 的标准做法是:

create role role_operator; grant select, insert, update, delete on Student to role_operator; grant select, insert, update, delete on Course to role_operator; grant select, insert, update, delete on SC to role_operator; exec sp_addrolemember 'role_operator', 'operator_user';

逻辑说明:角色是权限打包的最小单位。先建角色,把四张表的增删改查权限授给角色,再把操作员账号加入角色。这样后面加新人时,只需要执行一句 sp_addrolemember,不用逐表重新授权——无论课设还是生产,这个习惯都值得养成。

3.3 字段类型取舍:课设能过但生产要改的几个点

文档里的字符类型全部用的 char 而非 nchar/nvarchar,这在 SQL Server 里对中文不友好。char(8) 按字节算长度,一个中文字符占 2 字节,所以 char(8) 只能存 4 个汉字——姓名超过 4 个字直接插入失败。正确做法是用 nchar(8),按字符数算,能存 8 个汉字。

同样的,smalldatetime 的日期范围是 1900-01-01 到 2079-12-31,精度到分钟。对学生出生日期来说没问题,但如果系统要记录选课时间戳到秒级,就得用 datetime2 或 datetime。这些小细节课设打分不查,但等系统真跑起来遇到数据装不下的时候,返工成本远比现在改类型高。

4. 数据增删改与备份还原:四条 SQL 语句和 RESTORE 参数的实操细节

4.1 增删改查:课设文档里最容易被忽略的完整闭环

文档给出的增删改三条语句如下,注意顺序执行时它们是一条完整的演示链路:

insert into student values('04', '张伟', '男', '1990-2-3', '计算机'); update student set sdept = '人文科学' where sno = '04'; delete from student where sno = '04';

逻辑说明:插入一条学号为 04 的学生记录,更新其所在系,再删除该记录。执行顺序不能乱——先插、再改、最后删,每一步都可以用 SELECT 验证。文档里 insert 语句的日期值没加引号('1990-2-3'),SQL Server 能隐式转换,但规范写法应该写成 '1990-02-03',避免不同区域的日期格式解析差异引发歧义。

另外要提醒:这句话执行完后,04 号学生就不存在了。如果你想复现 update 的效果,得先重新插入再更新,否则 UPDATE 影响 0 行,看起来像「没执行」。我一般会在每条操作前先跑一条 SELECT 确认数据在不在,再执行变更——这是数据库增删改查里最基础也最有效的防呆习惯。

查询部分文档没给示例,但需求分析明确说了学生管理包含「查询」。常见的补全写法是按学号查成绩:

select Course.Cname, SC.Grade from SC join Course on Course.Cno = SC.Cno where SC.Sno = '04';

如果是按课程查选了该课的所有学生,则把 where 条件换成 SC.Cno。

4.2 备份与还原:先理解模糊备份,再动手执行

文档对备份原理的描述对应 SQL Server 2005 的「模糊备份」机制:备份时把当前数据页和事务日志一起写入备份集,不保证数据页之间的完全一致,还原时通过前滚未完成事务再回滚未提交事务,最终把数据库恢复到一致状态。这一段理论描述写得比较绕,但你要是能用自己的话复述,答辩会加分——它是理解 RESTORE 背后「为什么日志要一起备份」的关键。

实际命令分三步走:

-- 第一步:备份数据库 backup database info to disk = 'D:\info_backup.bak'; -- 第二步:查看备份集内的文件列表(确认逻辑文件名) restore filelistonly from disk = 'D:\info_backup.bak'; -- 第三步:还原数据库并移动物理文件到新路径 restore database TestDB from disk = 'D:\info_backup.bak' with move 'info' to 'D:\test\info.mdf', move 'info_log' to 'D:\test\info_log.ldf';

逻辑说明:第二步的 RESTORE FILELISTONLY 只读取备份集的文件列表,不实际还原任何数据——它会返回备份里包含的逻辑文件名(比如 info 和 info_log),这个信息是第三步 MOVE 参数的前提。第三步把逻辑文件名映射到新的物理路径,解决了「原路径不存在」或「不想覆盖原库」的问题。

4.3 MOVE 和 REPLACE:还原时最容易被问到的两个参数

MOVE 的作用是将备份集中的逻辑文件移动到指定物理位置。默认情况下,RESTORE 会把文件还原到备份时的原始位置;如果原路径在该机器上不存在,或者你想把数据库副本放到别的盘,就必须用 MOVE 重定向。

REPLACE 是强制覆盖。如果目标库已存在且同名,直接 RESTORE 会报错「数据库正在使用中」或「无法覆盖现有数据库」,这时需要加 REPLACE。两个参数经常同时出现:

restore database StudentDB from disk = 'D:\backup\student.bak' with replace, move 'student' to 'D:\data\student.mdf', move 'student_log' to 'D:\data\student_log.ldf';

注意 REPLACE 只解决「数据库已存在」的问题,不解决「正在被占用」的问题——后者需要先断开连接(第 5 章有完整处理)。另外,这套备份还原链路在很多数据库同步工具里也是同样的逻辑:工具本质上是帮你执行了 RESTORE + MOVE,看懂这里,后面排查同步工具的日志就不会一脸懵。

5. 避坑与排查:学号前导零、外键删除与还原冲突的五条经验

5.1 学号前导零被悄悄吃掉

现象:插入 '00001' 后 SELECT 出来变成 '1' 或 ' 1',按学号关联查询时匹配不上。

原因:char(5) 本身能存 '00001',问题出在中间层——最常见的场景是 VB 端把文本框内容用 Val() 转成了数值类型再传给数据库,Val("00001") 结果是 1,再塞进 char(5) 就变成了 '1 '。另一个场景是插入语句里写的是数值常量insert into Student values(00001, ...),SQL Server 会把 00001 当作整数 1 处理。

解决:应用层始终坚持字符串传参,连接数据库用参数化查询,参数类型用 adVarChar 而不是 adInteger;自己写 SQL 时学号字面量加引号,'00001'。如果学号本质是数字序号,不如直接把列类型改成 varchar(10) 或用 IDENTITY 自增,让数据库生成序号,应用层拿到后再补零显示。

5.2 RESTORE 报错:数据库正在使用中

现象:还原时提示「无法获得独占访问权,因为数据库正在使用中」,任务一直卡住或直接失败。

原因:有活动连接占用了目标数据库——可能是 SSMS 里打开着该库的查询窗口,也可能是 VB 程序跑完没关闭连接,甚至是你自己在还原前预览了表数据没关掉。

解决:还原前把数据库切成单用户模式并强制回滚未完成事务:

alter database StudentDB set single_user with rollback immediate; restore database StudentDB from disk = 'D:\backup\student.bak'; alter database StudentDB set multi_user;

这里要分开记:REPLACE 处理的是「数据库已存在」的冲突,SINGLE_USER 处理的是「连接占用」的冲突——两个坑不一样,混在一起排查会很浪费时间。

5.3 中文姓名在 char(8) 里被截断

现象:插入「欧阳娜娜」这种四个汉字的名字没问题,但插入五六个字的名字时报错「字符串或二进制数据将被截断」,或者数据写进去后变成乱码。

原因:SQL Server 的 char(8) 按字节算长度,一个中文字符占 2 字节,char(8) 实际只能存 4 个汉字。文档里的 Sname 用的是 char(8),存 5 个汉字必然溢出。

解决:字符类型一律用 nchar/nvarchar,按字符数定义长度,nchar(8) 能存 8 个汉字;建库时排序规则选 Chinese_PRC_CI_AS。这个坑在课设数据量小的时候不容易触发,但一旦有名字略长的真实数据进来就翻车,属于典型的「演示没问题、上线就炸」。

5.4 删不掉被选修的课程

现象:执行delete from Course where Cno='01'报错,提示外键约束冲突,引用行存在。

原因:SC 表里还有选课记录引用 Cno='01',外键约束不允许删除被引用的父表数据。这在数据库并发锁角度也是常见冲突来源之一——删除父表行时,子表的外键检查会持有锁,和正在写入 SC 的会话互相等待。

解决:先删子表再删父表:

delete from SC where Cno='01'; delete from Course where Cno='01';

执行前先跑 SELECT 确认破坏了哪些关联数据,再用事务包住两条 DELETE,出问题还能回滚。也可以在表设计时用 ON DELETE CASCADE 实现级联删除,但课设答辩里老师会追问 CASCADE 的副作用——如果选课记录是重要审计数据,级联删除会把历史全抹掉,慎用。

5.5 中文视图名在 ADO 连接下报「对象名无效」

现象:VB 程序用 ADO 连接 SQL Server,执行select * from 学生_成绩时报「对象名无效」或「列名无效」。

原因:视图名和列名用了中文标识符。SSMS 里中文标识符没问题,但 VB 的 ADO 走的是 ODBC 驱动链路,客户端字符集解析和服务器排序规则不一致时,中文标识符经常解析失败。这个现象很玄学,有时候同样的代码在一台机器能跑、另一台就报错。

解决:视图和列名强制用英文命名,比如 v_student_score、student_name;中文展示逻辑放前端界面,数据库层保持纯英文标识符。从那以后我建任何视图都不再用中文名——这不是 SQL Server 不支持,而是跨语言客户端时你赌不起字符集一致性。

6. 从课设到演示:一张验证清单把文档变成可运行的选课系统

6.1 用最小数据集验证约束和视图

按文档建完表后,我建议先跑这样一套验证脚本,确认约束和视图真实生效:

-- 1. 插入基础数据 insert into Student values('00001', '张三', '男', '2000-01-01', '计算机系'); insert into Course values('C01', '数据库原理', 3.0); insert into SC values('00001', 'C01', 88.5); -- 2. 验证外键:下面这条语句应当报错 insert into SC values('99999', 'C01', 90); -- 3. 验证 CHECK 约束:下面这条语句应当报错 insert into Student values('00002', '李四', '未知', '2000-02-01', '软件工程系'); -- 4. 验证视图:返回张三的选课成绩 select * from v_student_score;

这套脚本能证明三件事:外键拒绝不存在的学生、CHECK 拒绝非法性别、视图 join 正确关联三张表。答辩时把这四条依次跑给老师看,比嘴上说「系统设计完整」有力得多。

6.2 备份还原的完整演练脚本

数据库课程设计的评分表里「系统维护」占了明确分值,但很多组只写文档不演示。我一般会准备一条独立的还原演练库,避免动主库:

backup database StudentDB to disk = 'D:\backup\StudentDB.bak'; restore filelistonly from disk = 'D:\backup\StudentDB.bak'; restore database StudentDB_Demo from disk = 'D:\backup\StudentDB.bak' with move 'StudentDB' to 'D:\data\StudentDB_Demo.mdf', move 'StudentDB_log' to 'D:\data\StudentDB_Demo_log.ldf';

还原成功后,StudentDB 和 StudentDB_Demo 两个库并存,在 SSMS 里一眼能看出备份还原链路是通的。这一步演示完,系统维护这块的分数基本就稳了。

6.3 VB 侧的最小连接与启动顺序

文档的界面设计给了菜单规划,但没给数据访问代码。用 VB 连接 SQL Server,最小可行写法是 ADODB.Connection 直连:

Dim conn As New ADODB.Connection conn.ConnectionString = "Provider=SQLOLEDB;Data Source=localhost\SQLEXPRESS;Initial Catalog=StudentDB;User ID=sa;Password=***;" conn.Open

连接失败时按顺序排查:先 ping 数据库服务器,再用 SSMS 用同一账号尝试登录,最后检查 SQL Server 配置管理器里 TCP/IP 协议是否启用——默认安装下 TCP/IP 可能是禁用的,VB 这边连不上,但 SSMS 能连,就是这个原因。

这套验证链路走完,「文档里写的」和「机器上跑的」就对齐了。从那以后我每次拿到课设文档,都强制先跑一遍「建表 → 插数 → 查询 → 视图 → 备份 → 还原」的完整链路,确认能跑通再谈改代码。一份文档即使写得再完整,和你本机的 SQL Server 版本、排序规则、端口配置对不上,照样会翻车——先验证再动手,比什么都省时间。希望帮到你。

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

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

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

立即咨询