简介:这是一份基于SQL Server的学生选课系统数据库设计资料,适合高校数据库课程设计、期末大作业及入门级选课项目实践。项目包含带注释的SQL建库脚本与详细文档,对数据表结构、关系设计和功能模块均有说明,新手也能理解并按文档部署运行。压缩包共5个文件:sql脚本、docx设计文档、md说明文件,以及2张png界面示意图,整体仅138KB,轻量便于快速查阅。当前已有432人学习下载,口碑集中在满分大作业、高分项目方向。下载后可获得完整选课管理核心实现、数据库设计思路、部署说明与界面图示,足以支撑课程汇报、答辩演示或二次功能扩展。
1. 基于SQL Server的学生选课系统数据库设计:为什么跑通源码不算数
答辩前三天才拿到这份“高分项目”源码,是很多学生做数据库课程设计时的真实处境。但真正让SQL Server学生选课系统翻车的,往往不是INSERT和SELECT写错,而是建表之前没人把业务边界想清楚。这个标题背后其实是一整套“从E-R模型到物理建表、从存储过程到测试数据、再从脚本到答辩文档”的工作流,它能解决的不只是“交一份能跑的源码”,而是让你在老师追问“为什么这张表这么设计、锁怎么加、索引建在哪”时答得上来。适合正在做课程设计、毕业设计或想按工程方式重做一遍选课系统的人,照这套路径走,新手能跟,老手能省。
2. 需求模型先行:把选课系统的业务边界拆清楚再落库
2.1 没有画E-R图就建表,后期基本要返工
学生选课系统最核心的业务关系,是“学生”和“课程”之间的多对多联系。一个学生可以选多门课,一门课可以被多个学生选,这在数据库里不能直接靠两张表加外键表达,必须引入第三张“选课记录表”。很多初学者把选课表做成“一个学生一条记录、多个课程字段拼在一起”,这相当于把一个M:N关系强行压成1:N,后期要加成绩、加退课状态、加选课学期时,只能整表推倒重来。
我在画E-R图时,习惯先把实体和关键属性列成一张表,确认后再动CREATE TABLE。下面这张属性表也建议直接抄进设计文档,它是后期写数据字典的底稿。
| 实体 | 关键属性 | 关系 |
|---|---|---|
| 学生 | 学号、姓名、性别、年级、专业 | 与课程构成 M:N |
| 课程 | 课程号、课程名、学分、容量、学期 | 与学生构成 M:N,与教师 N:1 |
| 教师 | 教师号、姓名、职称、院系 | 与课程 1:N |
| 选课记录 | 学号、课程号、选课时间、成绩、状态 | M:N 的关联实体 |
这里有一个容易被忽略的点:选课记录不只是连接表,它自身带属性。选课时间、成绩、退课状态、退课原因都是选课记录的属性,不属于学生也不属于课程。所以E-R图里它必须是一个独立实体,而不是一条单纯的连线。把这个层级说清楚,设计文档的“概念设计”部分就站得住脚。
另外,别把班级、专业这类弱实体想得太复杂。课程设计阶段的评分重点是看你能不能把M:N转换、1:N归属、属性归属讲明白,而不是把系统做成ERP。教师和课程是1:N,教师可以上多门课;如果你不需要排课和工资核算,教师表不必和班级表做任何关联,避免无意义的循环依赖。
2.2 第三范式为主、反范式为辅:冗余字段放哪不放哪
表结构设计的基本原则是“先满足第三范式,再谈冗余”。放到选课系统里,最直接的体现是:选课记录表只存学号、课程号、选课时间、成绩、状态,课程名称去课程表查,学生姓名去学生表查。这样做的好处是数据源唯一,如果课程改名或教师换人,历史记录不会跟着乱掉。
但“绝对不冗余”也不是满分答案。我见过一个真实场景:课程在学期结束后停开,成绩单要从选课记录里出,如果选课记录不冗余课程名,课程一旦从课程表删除或被改成新课号,历史成绩单上就查不到当年到底上的什么课。这时把“课程名快照”冗余进选课记录,反而是合理设计。所以范式是工具,不是教条,反范式只用在你确实需要对抗“历史数据变化”的地方。
还有一个典型的冗余误区是“在课程表里维护已选人数”。用UPDATE Course SET EnrolledCount = EnrolledCount + 1的方式来计数,在高并发选课场景下几乎必然出现丢失更新。而且这个字段本身就是冗余,可查COUNT(*)得到。我在这个项目里一律不维护这类计数列,宁可让统计查询多扫几页,也不要让写路径背着不一致的风险。
2.3 关键业务规则:课时上限、时间冲突、退课与加退选状态机
动手建表前,必须把业务规则列出来,这些规则决定了约束和存储过程怎么写。常见的学生选课系统至少要覆盖四条:每学期选课门数或学分上限,一般由应用层校验;课程容量约束,数据库层用CREATE TABLE时的CHECK约束加上选课存储过程的计数判断;同一学生同一课程不能重复选,用唯一索引兜底;退课不是物理删除,而是状态流转。
状态流转是我特别想强调的一个设计点,它直接关系到表的可用性。选课记录的Status字段我定义为:1代表已选、0代表退课、2代表已结课或成绩锁定。退课时执行UPDATE把Status置为0,而不是DELETE FROM Enrollment,这样保留选课历史,老师复查数据时能看到“这个学生选过又退了”的完整轨迹。选课后重新选同一门课时,再把Status从0恢复成1,而不是插入一条新记录。
为什么不用DELETE?除了审计历史之外,唯一索引也是原因。如果你给(StudentID, CourseID)加了唯一索引,退课用DELETE会留下空位可以再INSERT,看似没问题;但一旦成绩已经录入、学生已经结课,这条记录就不能删。用状态机配合唯一索引,数据永远在“一条学生课程对,多个状态”的框架下流转,不会自己把自己锁死。
3. 用T-SQL把设计落成物理表:建库脚本与字段级参数
3.1 建库脚本:数据文件与日志文件的路径、增长和排序规则
建立数据库这件事看起来简单,但我见过太多人用SSMS图形界面点两下就完事,最后交付时只有一个数据库文件,连脚本都拿不出来。课程设计项目里,建库脚本必须可重放,而且要讲得出为什么要这样设。
-- 01_CreateDatabase.sql CREATE DATABASE StudentCourseDB ON PRIMARY ( NAME = N'StudentCourseDB', FILENAME = N'C:\SQLData\StudentCourseDB.mdf', SIZE = 16MB, MAXSIZE = UNLIMITED, FILEGROWTH = 16MB ) LOG ON ( NAME = N'StudentCourseDB_log', FILENAME = N'C:\SQLData\StudentCourseDB_log.ldf', SIZE = 8MB, MAXSIZE = 512MB, FILEGROWTH = 8MB ) COLLATE Chinese_PRC_CI_AS; GO这段脚本里值得讲解的参数有三个。FILENAME指定的路径必须提前存在,否则CREATE DATABASE直接报错,这是新手最常见的建库翻车点。MAXSIZE给日志文件设上限为512MB,而不是UNLIMITED,能防止一次大事务把日志文件撑到几十GB;数据文件可以UNLIMITED,但日志文件建议设上限,这是血泪经验。COLLATE选Chinese_PRC_CI_AS,表示按中文拼音排序、大小写不敏感,配合后面NVARCHAR字段能彻底避免中文乱码和排序错乱。
提示:如果你的SQL Server实例装在默认路径,可以把FILENAME改成SQL Server安装目录下的DATA文件夹,或干脆用相对路径,脚本在你自己机器上重放时更不容易因为盘符不存在而失败。
3.2 从用户信息表开始的建表脚本:主键、默认值、检查约束
很多课程设计的第一张表都是用户信息表,但比表名更重要的是字段类型和约束怎么选。这里我给出学生、课程、选课记录三张核心表的建表脚本,这也是整个项目里被老师追问最多的一段。
-- 02_CreateTables.sql USE StudentCourseDB; GO CREATE TABLE Student ( StudentID NVARCHAR(20) NOT NULL CONSTRAINT PK_Student PRIMARY KEY, StudentName NVARCHAR(50) NOT NULL, Gender NCHAR(1) NOT NULL CONSTRAINT DF_Student_Gender DEFAULT N'男' CONSTRAINT CK_Student_Gender CHECK (Gender IN (N'男', N'女')), BirthDate DATE NULL, Major NVARCHAR(50) NULL, Grade INT NULL CONSTRAINT CK_Student_Grade CHECK (Grade >= 2000 AND Grade <= 2035), Phone NVARCHAR(20) NULL, Email NVARCHAR(100) NULL, EnrollDate DATETIME NOT NULL CONSTRAINT DF_Student_EnrollDate DEFAULT GETDATE(), Status TINYINT NOT NULL CONSTRAINT DF_Student_Status DEFAULT 1 ); GO CREATE TABLE Course ( CourseID NVARCHAR(20) NOT NULL CONSTRAINT PK_Course PRIMARY KEY, CourseName NVARCHAR(100) NOT NULL, Credit DECIMAL(3,1) NOT NULL CONSTRAINT CK_Course_Credit CHECK (Credit > 0 AND Credit <= 10), ClassHours INT NULL, Capacity INT NOT NULL CONSTRAINT DF_Course_Capacity DEFAULT 60 CONSTRAINT CK_Course_Capacity CHECK (Capacity > 0), TeacherID NVARCHAR(20) NULL, Schedule NVARCHAR(100) NULL, Location NVARCHAR(100) NULL, Semester NVARCHAR(20) NOT NULL, Description NVARCHAR(500) NULL ); GO CREATE TABLE Enrollment ( EnrollmentID INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Enrollment PRIMARY KEY, StudentID NVARCHAR(20) NOT NULL, CourseID NVARCHAR(20) NOT NULL, SelectedTime DATETIME NOT NULL CONSTRAINT DF_Enrollment_SelectedTime DEFAULT GETDATE(), Score DECIMAL(5,1) NULL, Status TINYINT NOT NULL CONSTRAINT DF_Enrollment_Status DEFAULT 1, CONSTRAINT CK_Enrollment_Score CHECK (Score IS NULL OR (Score >= 0 AND Score <= 100)), CONSTRAINT CK_Enrollment_Status CHECK (Status IN (0, 1, 2)) ); GO先说类型。学生表的学号用NVARCHAR而不用INT,因为学号不参与算术运算,而且前导零是有效信息,存成INT会丢。所有中文字段统一用NVARCHAR或NCHAR,配合字符串字面量前的N前缀,这是避免乱码的标准姿势。Gender用NCHAR(1)加检查约束,比用BIT存性别可读性好得多,文档里也能直接说明“该字段取值范围是男和女”。
再讲约束。CHECK约束写在列上是SQL Server 2019之后比较清爽的写法,Credit用DECIMAL(3,1)能存0.5这种半学分。Score允许NULL,所以检查约束必须写成Score IS NULL OR (Score >= 0 AND Score <= 100),漏掉IS NULL判断会导致没录成绩时插入失败。Status的3个取值和选课状态机对应,注释里写明白即可。
Enrollment表我用IDENTITY自增列做主键,而不是用(StudentID, CourseID)做复合主键。原因是选课记录没有天然的业务主键,而且自增主键能让外键引用、分页查询都更简单;唯一性约束交给后面的唯一索引去承担,职责更清晰。这里故意不给Enrollment加联合主键,是想让增删改查的写法更接近生产环境习惯。
3.3 外键往哪加:删除策略怎么定才不会被老师问倒
外键是关系数据库的尊严所在。这个项目里涉及三组关系需要外键:Course表引用Teacher表、Enrollment表引用Student和Course表。下面脚本就是完整的关联关系落库。
-- 03_AddForeignKeys.sql USE StudentCourseDB; GO CREATE TABLE Teacher ( TeacherID NVARCHAR(20) NOT NULL CONSTRAINT PK_Teacher PRIMARY KEY, TeacherName NVARCHAR(50) NOT NULL, Title NVARCHAR(30) NULL, Department NVARCHAR(50) NULL ); GO ALTER TABLE Course ADD CONSTRAINT FK_Course_Teacher FOREIGN KEY (TeacherID) REFERENCES Teacher(TeacherID) ON DELETE SET NULL; GO ALTER TABLE Enrollment ADD CONSTRAINT FK_Enrollment_Student FOREIGN KEY (StudentID) REFERENCES Student(StudentID); GO ALTER TABLE Enrollment ADD CONSTRAINT FK_Enrollment_Course FOREIGN KEY (CourseID) REFERENCES Course(CourseID); GO这段设计里最值得跟老师解释的是删除策略。Enrollment的两条外键我刻意不写ON DELETE CASCADE,因为选课记录是审计性质的数据,删掉学生或课程时连带删掉成绩记录,属于不可恢复的事故。SQL Server默认行为是NO ACTION,也就是有选课记录引用时,学生和课程删不掉,这反而是一种保护。
Course引Teacher用ON DELETE SET NULL,逻辑是教师离职后课程还保留,只是TeacherID置空。为什么不用CASCADE?因为一门课停开不等于要删掉所有选课记录,两者生命周期不同。回答这类“删除策略怎么定”的问题时,能说清“我先分析数据生命周期,再选策略”,比背出三种级联方式加分得多。
4. 选课业务不能全靠裸SQL:存储过程、视图与索引的配合
4.1 选课存储过程:事务、行锁与容量判断一次性做掉
选课是高并发写入场景,最经典的错误是从应用层先SELECT Capacity,再判断人数,再INSERT。这条路径上没有锁保护,两个连接可能同时读到容量还剩1个,然后同时插入成功,最终超员。正确做法是把选课逻辑收进存储过程,用事务和锁把容量判断和插入绑在一条线上。
-- 04_CreateProcedures.sql USE StudentCourseDB; GO CREATE OR ALTER PROCEDURE usp_EnrollCourse @StudentID NVARCHAR(20), @CourseID NVARCHAR(20), @Semester NVARCHAR(20) AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; DECLARE @Capacity INT, @Enrolled INT; BEGIN TRY BEGIN TRANSACTION; -- 锁定课程行,让同一门课的并发选课排队执行 SELECT @Capacity = Capacity FROM Course WITH (UPDLOCK, HOLDLOCK) WHERE CourseID = @CourseID AND Semester = @Semester; IF @Capacity IS NULL BEGIN RAISERROR(N'课程不存在或学期不匹配', 16, 1); ROLLBACK; RETURN; END -- 统计当前状态为1的有效选课人数 SELECT @Enrolled = COUNT(*) FROM Enrollment WHERE CourseID = @CourseID AND Status = 1; IF @Enrolled >= @Capacity BEGIN RAISERROR(N'课程容量已满', 16, 2); ROLLBACK; RETURN; END -- 已选状态直接报重复;退课状态则恢复;无记录则新增 IF EXISTS (SELECT 1 FROM Enrollment WHERE StudentID = @StudentID AND CourseID = @CourseID AND Status = 1) BEGIN RAISERROR(N'请勿重复选课', 16, 3); ROLLBACK; RETURN; END IF EXISTS (SELECT 1 FROM Enrollment WHERE StudentID = @StudentID AND CourseID = @CourseID AND Status = 0) BEGIN UPDATE Enrollment SET Status = 1, SelectedTime = GETDATE() WHERE StudentID = @StudentID AND CourseID = @CourseID; END ELSE BEGIN INSERT INTO Enrollment(StudentID, CourseID, Status) VALUES (@StudentID, @CourseID, 1); END COMMIT; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK; THROW; END CATCH END GO调用方式为EXEC usp_EnrollCourse '2024001', 'C001', '2024-2025-1',第二次对同一学生同一课程执行时会抛“请勿重复选课”,正好验证约束生效。
这段代码里最值钱的是WITH (UPDLOCK, HOLDLOCK)。UPDLOCK让SQL Server对Course表的这一行加更新锁,HOLDLOCK把锁持有到事务结束,这样两个并发连接同时执行这个存储过程时,第二个连接会等第一个提交后才读容量,彻底解决超选。锁粒度控制在一行课程,而不是锁整张表,并发度比表锁高得多。
SET XACT_ABORT ON的作用是遇到运行错误立即回滚整个事务,否则SQL Server默认只回滚出错的那条语句,事务可能残留在未提交状态。THROW是SQL Server 2012起可用的错误抛出方式,它能保留原始错误信息,比只写RAISERROR更好定位问题。课程设计里这个存储过程本身就可以作为“并发控制”章节的演示案例写进文档。
4.2 视图:把三表关联封装成成绩单和选课统计
视图存在的意义是让查询变成可交付物。学生成绩单要连接学生、课程、选课记录三张表,如果每次都在应用层写JOIN,SQL分散且难维护。我把两个最常用的查询固定成视图,文档里也能直接引用。
-- 05_CreateViews.sql USE StudentCourseDB; GO CREATE OR ALTER VIEW v_StudentScore AS SELECT s.StudentID, s.StudentName, c.CourseID, c.CourseName, c.Credit, e.Score, c.Semester FROM Student s INNER JOIN Enrollment e ON s.StudentID = e.StudentID AND e.Status = 1 INNER JOIN Course c ON e.CourseID = c.CourseID; GO CREATE OR ALTER VIEW v_CourseEnrollCount AS SELECT c.CourseID, c.CourseName, c.Semester, c.Capacity, COUNT(e.EnrollmentID) AS EnrolledCount FROM Course c LEFT JOIN Enrollment e ON c.CourseID = e.CourseID AND e.Status = 1 GROUP BY c.CourseID, c.CourseName, c.Semester, c.Capacity; GO成绩单视图中把Status限定为1,退课和已结课的成绩不会混进来,保证“当前有效选课”体现的是实时状态。规划选课统计视图时,我用LEFT JOIN保留那些没人选的课程,不然选课人数为0的课程会直接从结果里消失。统计人数时必须写COUNT(e.EnrollmentID),不能写COUNT(*),否则无人选课时也返回1,这个细节是很多人在演示时才发现结果对不上的原因。
视图不建索引时性能一般,但课程设计的数据量几百条,查询足够快。如果嫌慢,可以用WITH SCHEMABINDING建索引视图,我不会建议你用,因为索引视图的限制多、维护成本高,用它去应对课程设计属于杀鸡用牛刀。
4.3 索引参数:建在哪、加不加INCLUDE、唯一索引怎么用
索引不是越多越好,而是要为查询服务。这个系统里最重要的查询路径有三个:按(StudentID, CourseID)查选课记录,按Status统计人数,按Semester查课程。下面索引脚本和这三条路径一一对应。
-- 06_CreateIndexes.sql USE StudentCourseDB; GO -- 唯一索引:同一学生同一课程只允许一条记录 CREATE UNIQUE INDEX IX_Enrollment_Student_Course ON Enrollment(StudentID, CourseID); -- 过滤状态栏的普通索引,带上CourseID减少回表 CREATE INDEX IX_Enrollment_Status ON Enrollment(Status) INCLUDE (CourseID); -- 按学期筛选课程是最常见的入口 CREATE INDEX IX_Course_Semester ON Course(Semester, CourseID); GO唯一索引承担了第2章说的“最后一防线”角色。应用层可以不判断重复选课,数据库层面这条索引直接拒绝第二条同学生同课程的记录;但注意它和表上的CHECK约束不同,它不管Status,所以靠存储过程里“退课状态恢复”的逻辑来配合。INCLUDE (CourseID)是把CourseID作为索引的包含列,查询SELECT CourseID、WHERE Status时不用回表读数据页,属于覆盖索引的简单实现。
CREATE UNIQUE INDEX如果建在已有重复数据的表上会直接失败,所以这个索引要在数据初始化之前建。FILLFACTOR参数我一般会设80,它的含义是索引页预留20%空间给新插入的键值,减少页分裂;如果你的选课系统写多读少,这个值是有用的,纯读场景设100就行。别在Enrollment的StudentID、CourseID之外再堆一堆单列索引,学生选课系统写入频繁,每个索引都拉低INSERT速度,用最少的索引覆盖最关键的查询才是高分答案。
5. 学生选课系统数据库常见问题避坑:日志、登录、并发与中文乱码
5.1 中文乱码:VARCHAR和排序规则一起背锅
现象:插入“张三”后查询显示“??”,或者在SSMS里手动插入中文正常,程序插入就乱码。原因通常是字段用了VARCHAR,SQL Server按代码页把中文字符转换成了无法识别的字节;还有一个原因是数据库排序规则不是中文系列,默认的Latin1_General对中文不友好。解决:建表字段统一NVARCHAR/NCHAR,字符串常量前加N前缀,库级COLLATE用Chinese_PRC_CI_AS。如果已经建错库,可以执行ALTER DATABASE StudentCourseDB COLLATE Chinese_PRC_CI_AS,但改排序规则对已有字符串数据不一定全部生效,最稳的办法是重建表前先确认。用SELECT name, collation_name FROM sys.databases能看到当前库的排序规则,这个检查动作写进文档里也是加分项。
5.2 并发选课超员:人数判断形同虚设
现象:两个学生同时点选同一门只剩1个名额的课,两边都提示选课成功,最后课程表里超员。原因:应用层执行的是“先SELECT容量、再INSERT”两条独立SQL,中间没有事务和锁,两个连接读到同一个剩余名额,随后都插入了。解决:按第4章的做法,把容量判断和插入收进一个事务存储过程,用UPDLOCK和HOLDLOCK锁住课程行。这里特别提醒:只加BEGIN TRAN不加锁没用,因为默认的读操作在读已提交隔离级别下不加锁,读到的是旧版本数据,锁必须显式加在课程行上。
5.3 删除课程被外键拦截:物理删除不如逻辑删除
现象:执行DELETE FROM Course WHERE CourseID = 'C001',SQL Server报错“DELETE语句与REFERENCE约束冲突”。原因:Enrollment表还有该课程的选课记录,外键默认NO ACTION阻止删除。解决:先处理子表记录再删主表,或干脆不物理删除。处理脚本是DELETE FROM Enrollment WHERE CourseID = 'C001',但这是暴力清空选课历史。更好的方案是给Course表加IsActive字段,停用课程时UPDATE为0而不是DELETE。这样设计文档里能写出一句很显水平的话:“主数据采用逻辑删除,保留业务历史完整性。”
5.4 登录18456:本地库都连不上就离谱
现象:SSMS连接本机SQL Server实例报18456,Login failed for user 'sa'。原因:安装时选了Windows身份验证模式,SQL Server登录被禁用;或者sa账号本身被禁用。解决:先用Windows认证方式登录SSMS,在服务器属性→安全性里改成“SQL Server和Windows身份验证模式”,然后到安全→登录名→sa里启用账号并设置新密码。连接字符串也要配套,托管应用里常见写法是Server=.;Database=StudentCourseDB;User Id=sa;Password=你的密码;TrustServerCertificate=True。这个坑如果在答辩现场现场连不上,会非常尴尬,建议提前一天做一次“清空连接字符串重新登录”的演练。
5.5 SSMS版本和SSL证书链:客户端连服务器的两个隐蔽坑
现象:新装的SSMS连接SQL Server 2022时报ODBC Driver 18的SSL错误,类似“证书链是由不受信任的颁发机构颁发的 (-2146893019)”。原因:SQL Server 2022默认强制加密连接,而开发机没有安装服务器自签名证书到受信任根。解决:测试环境连接时在连接对话框的“选项→加密”里勾选“Trust server certificate”,这是最快捷的绕过方式;生产环境则要把服务器端证书安装到客户端受信任根证书存储区。另外SSMS版本太低也会连不上新实例,SSMS 18.x管理SQL Server 2022会有兼容问题,建议统一升级到20.x再排查。这类问题其实和SQL代码无关,但最容易在演示前夜把人逼疯,属于典型的交付期玄学。
6. 高分项目的价值在验证:一键重放、造数、关系图与答辩细节
6.1 给脚本排好编号,删库重放全自动
我通常不交付一个孤零零的.bak备份文件,而是把建库、建表、加外键、建视图、建索引、存储过程、测试数据按编号排成一系列脚本,然后整库删掉重放一次。演示给老师看“我的数据库可以随时重建”,比展示备份文件有说服力得多。
sqlcmd -S . -E -i 01_CreateDatabase.sql sqlcmd -S . -E -d StudentCourseDB -i 02_CreateTables.sql sqlcmd -S . -E -d StudentCourseDB -i 03_AddForeignKeys.sql sqlcmd -S . -E -d StudentCourseDB -i 04_CreateProcedures.sql sqlcmd -S . -E -d StudentCourseDB -i 05_CreateViews.sql sqlcmd -S . -E -d StudentCourseDB -i 06_CreateIndexes.sqlsqlcmd的-S .表示本机默认实例,-E用Windows认证,-i指定脚本文件。重放前先执行DROP DATABASE StudentCourseDB,保证脚本从零开始。测试数据用WHILE循环批量生成,比如生成50个学生、20门课、300条选课记录,注意覆盖已选、退课、已结课三种状态,这样成绩单视图和选课统计结果才完整。
6.2 文档里放什么:关系图、统计IO和一张表
高分文档不是源码的打印版,而是“源码+笔记”的对应体。我在SSMS里右键数据库→数据库关系图→新建关系图,把三张表的外键关系截图放进设计文档,一图胜千言。再配合SET STATISTICS IO ON; SET STATISTICS TIME ON;跑两条查询,把逻辑读和CPU时间截图放进“性能验证”小节,老师很难不给高分。
| 文档章节 | 对应交付物 |
|---|---|
| 需求分析 | 业务规则与用例 |
| 概念设计 | E-R 图与实体属性表 |
| 逻辑设计 | 表结构、约束、视图定义 |
| 物理设计 | 文件路径、索引、存储过程 |
| 测试与演示 | 运行截图、统计 IO |
我这些年养成的习惯是:任何数据库项目交付前先删库再重放一遍,然后检查关系图和统计IO截图,最后才打包。这个习惯救过我很多次,有一次就是唯一索引和退课状态冲突,重放测试数据时才暴露出来。希望帮到你。
本文还有配套的精品资源,点击获取