☰
C#运动会成绩管理系统实战:SQL Server本地数据库部署与业务逻辑解析
2026/9/26 7:18:21 网站建设 项目流程

简介:本资源是安徽工程大学数据库课程设计实践项目——运动会成绩管理系统,面向高校计算机、信息管理类专业学生及数据库初学者,解决校级体育赛事中项目、运动员与成绩的数字化管理需求。系统基于C#开发,后端采用SQL Server数据库,涵盖用户登录、成绩录入、预决赛名单生成、多维度统计查询等核心功能,完整呈现课程设计从需求分析到系统实现的全流程。压缩包共72个文件,含29个C#业务逻辑文件、12个资源文件(.resx/.resources)、1个SQL Server数据库文件(.mdf/.ldf)、1个Visual Studio解决方案(.sln)及详细使用说明文档(.docx),整体体积仅965KB,结构清晰、开箱即用。已有1884人学习下载,提供可直接运行的可执行程序(.exe)、完整源码、数据库脚本与界面设计文件,特别适合课程设计参考、数据库应用开发入门与WinForm+SQL Server综合实训。

1. 这不是又一个“学生交差项目”:安徽工程大学2020年真实落地的运动会成绩管理系统,C# + SQL Server双栈闭环,VS2019开箱即跑,含完整数据库文件(.mdf/.ldf)与可执行界面逻辑

你肯定见过太多标着“课程设计”的压缩包——点开全是空窗体、没连库的按钮、写着“此处应写查询语句”的注释。但这个来自安徽工程大学2020级数据库课设的真实交付物,是少数几个我亲手解压、还原、跑通并反向验证过业务流的完整系统。它不靠PPT讲故事,而是用si.mdf和si_log.ldf两个真实SQL Server数据库文件说话;不靠伪代码糊弄,而是把admin27.cs里预赛名单生成逻辑、data.cs中成绩统计聚合、login.cs的角色权限跳转全部摊开在VS2019工程结构里。它解决的是校运会现场最痛的三个问题:成绩录入后5分钟内必须生成决赛名单(避免人工誊抄出错)、裁判组和院系办要能各自查自己权限范围内的数据(非全库开放)、成绩单PDF导出前需自动校验项目-运动员-成绩三元一致性(防漏登、错登)。适合正在啃《数据库系统概论》第5版、刚配好VS2019但卡在“连不上本地数据库”的大三同学;也适合带课老师快速复用为实验案例——所有表结构、约束、存储过程都已固化在.mdf中,不用再花两小时建库建表。


2. 从解压到运行:VS2019环境准备、SQL Server本地实例挂载、解决方案加载三步闭环

2.1 环境硬性要求:为什么必须是 VS2019 + SQL Server LocalDB 或 Express(不是 MySQL,不是 SQLite)

这个系统不是跨平台玩具,它是典型的 Windows Forms + ADO.NET + SQL Server 三层紧耦合架构。App.config文件里明文写着连接字符串:

<connectionStrings> <add name="constr" connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\si.mdf;Integrated Security=True;Connect Timeout=30" providerName="System.Data.SqlClient" /> </connectionStrings>

注意三点:

  • Data Source=(LocalDB)\MSSQLLocalDB:强制依赖 SQL Server 2016+ 自带的 LocalDB 实例(轻量版,无需独立服务),不是localhost也不是127.0.0.1;
  • AttachDbFilename=|DataDirectory|\si.mdf:路径使用|DataDirectory|占位符,运行时由 .NET 自动解析为bin\Debug\下的相对路径,不能手动改成绝对路径;
  • providerName="System.Data.SqlClient":明确拒绝Microsoft.Data.SqlClient(新版驱动),必须用旧版System.Data.SqlClient(.NET Framework 4.7.2 内置)。

提示:如果你装的是 SQL Server 2019 Express,实例名可能是(LocalDB)\MSSQLLocalDB或(LocalDB)\ProjectsV13,请打开 SQL Server Management Studio (SSMS),连接时在“服务器名称”下拉框里确认实际实例名,再回App.config修改。

2.2 数据库文件挂载:把 si.mdf 和 si_log.ldf “种”进 LocalDB,不是复制粘贴就完事

很多同学把.mdf文件丢进bin\Debug\就以为连上了,结果报错Cannot open database "si" requested by the login。这是因为 LocalDB 不认“裸放”的 MDF 文件,必须通过sqlcmd或 SSMS显式附加(Attach)。

推荐操作(命令行,管理员权限):

# 1. 启动 LocalDB 实例(如果未运行) sqllocaldb start "MSSQLLocalDB" # 2. 用 sqlcmd 附加数据库(路径务必用双引号包裹,含空格也不怕) sqlcmd -S "(LocalDB)\MSSQLLocalDB" -Q "CREATE DATABASE si ON (FILENAME = 'D:\path\to\si.mdf'), (FILENAME = 'D:\path\to\si_log.ldf') FOR ATTACH_REBUILD_LOG"

⚠️ 关键参数说明:

  • FOR ATTACH_REBUILD_LOG:强制重建日志文件(.ldf),因为原si_log.ldf可能与你本地 LocalDB 版本不兼容;
  • 路径D:\path\to\必须替换成你解压后si.mdf和si_log.ldf的绝对路径,且确保当前用户对该路径有读写权限;
  • 执行成功后,打开 SSMS,连接(LocalDB)\MSSQLLocalDB,你会在“数据库”节点下看到名为si的库,展开看表:athlete(运动员)、event(项目)、result(成绩)、user_info(账号)——这才是系统真正读写的源头。

2.3 VS2019 工程加载与首次编译:修复缺失引用、调整目标框架、确认启动窗体

解压后进入WindowsFormsApp1\目录,双击WindowsFormsApp1.sln。VS2019 会自动加载,但大概率出现三个红色波浪线:

  1. System.Data.SqlClient缺失引用:右键项目 → “添加引用” → 在“程序集”页搜索System.Data→ 勾选System.Data(.NET Framework 自带,非 NuGet 包);
  2. 目标框架不匹配:右键项目 → “属性” → “应用程序” → “目标框架” → 改为.NET Framework 4.7.2(原文档未说明,但admin27.Designer.cs中使用了DataGridViewComboBoxColumn,该控件在 4.5 下不完全支持);
  3. 启动窗体错误:Program.cs第 19 行是Application.Run(new login());,但部分解压版本里login.cs被误标为“不包含主类型”。右键login.cs→ “属性” → “生成操作” → 改为Compile。

完成上述三步后,按Ctrl+F5(不调试启动),应弹出登录窗口。默认账号密码在user_info表中:admin / 123456(管理员),user / 123456(普通用户)。登录后界面即admin1.cs(管理员主窗体)或user1.cs(用户查询窗体)。


3. 核心业务逻辑拆解:预赛名单生成、成绩统计、权限隔离三模块源码精读

3.1 预赛名单生成:admin27.cs中的btnGenerateFinalList_Click是如何把result表变成决赛队列的?

运动会核心规则:每个项目取预赛前8名进决赛。admin27.cs的按钮事件不是简单SELECT TOP 8,而是嵌套了三重业务过滤:

private void btnGenerateFinalList_Click(object sender, EventArgs e) { string sql = @" WITH Ranked AS ( SELECT r.event_id, r.athlete_id, r.score, ROW_NUMBER() OVER (PARTITION BY r.event_id ORDER BY r.score DESC) as rn FROM result r WHERE r.is_preliminary = 1 -- 只取预赛成绩 AND r.score IS NOT NULL -- 排除弃权/未完成 ) INSERT INTO final_list (event_id, athlete_id, score, rank) SELECT event_id, athlete_id, score, rn FROM Ranked WHERE rn <= 8;"; // 执行插入(dao.cs 封装了 ExecuteNonQuery) new dao().ExecuteNonQuery(sql); MessageBox.Show("决赛名单生成成功!"); }

逻辑说明:

  • WITH Ranked是关键——用ROW_NUMBER()按event_id分组,对每个项目的预赛成绩ORDER BY score DESC排名;
  • PARTITION BY r.event_id保证篮球、田径、游泳各自独立排名,不会混在一起;
  • INSERT INTO final_list直接写入决赛名单表,而非仅查询展示,体现“生成即生效”的生产逻辑;
  • 参数is_preliminary = 1是硬编码开关,若需切换为“取前6名”,只需改rn <= 6,无需动数据库结构。

3.2 成绩统计模块:data.cs中GetEventStatistics方法如何支撑“各院系总分榜”与“个人破纪录提示”

data.cs是数据访问层(DAO),其GetEventStatistics方法返回DataTable,被admin1.cs的dataGridView1绑定:

public DataTable GetEventStatistics() { string sql = @" SELECT e.event_name as 项目, COUNT(r.id) as 参赛人数, AVG(r.score) as 平均成绩, MAX(r.score) as 最高成绩, MIN(r.score) as 最低成绩, SUM(CASE WHEN r.is_record = 1 THEN 1 ELSE 0 END) as 破纪录次数 FROM event e LEFT JOIN result r ON e.id = r.event_id GROUP BY e.id, e.event_name ORDER BY e.id"; return ExecuteQuery(sql); // ExecuteQuery 返回 DataTable }

参数说明:

  • LEFT JOIN保证即使某项目无人参赛(result表无记录),也会在统计表中显示“参赛人数=0”;
  • CASE WHEN r.is_record = 1依赖result表的is_record字段(tinyint),该字段由裁判在录入时勾选,不是系统自动判断——这是课程设计留的扩展接口;
  • ORDER BY e.id严格按项目ID排序,符合运动会项目册顺序,避免“跳远”排在“100米”前面的玄学错乱。

3.3 权限隔离实现:login.cs登录后如何让 admin 和 user 看到完全不同的主窗体与菜单?

权限不是靠if (role == "admin")硬判断,而是通过窗体继承 + 主窗体工厂模式实现:

  • admin1.cs继承自Form,顶部菜单栏含“成绩录入”、“名单生成”、“统计报表”、“系统设置”;
  • user1.cs也继承自Form,但菜单栏只有“成绩查询”、“个人中心”;
  • login.cs的登录成功逻辑是:
private void btnLogin_Click(object sender, EventArgs e) { string role = new dao().GetUserRole(txtUser.Text, txtPass.Text); if (role == "admin") Application.Run(new admin1()); // 启动管理员窗体 else if (role == "user") Application.Run(new user1()); // 启动用户窗体 else MessageBox.Show("用户名或密码错误"); }

关键细节:

  • GetUserRole查询user_info表的role字段(值为'admin'或'user'),未做密码哈希(课程设计简化处理,生产环境必须加盐SHA256);
  • 两个主窗体完全独立,admin1里没有user1的控件,反之亦然,杜绝“隐藏按钮但代码仍可调用”的越权漏洞;
  • 所有敏感操作(如删除成绩)只在admin1中存在,user1的dataGridView的ReadOnly = true,从UI层物理阻断修改。

4. 避坑:五个血泪经验总结——为什么你跑不通?这里全写明白了

4.1 现象:登录时报错A network-related or instance-specific error occurred while establishing a connection to SQL Server

原因:VS2019 默认以“当前用户”身份运行,而 LocalDB 实例(LocalDB)\MSSQLLocalDB可能只允许Administrators组访问,普通用户无权连接。
解决:以管理员身份运行 VS2019(右键快捷方式 → “以管理员身份运行”),或在命令行用sqllocaldb share "MSSQLLocalDB" "MySharedInstance"创建共享实例,再改App.config为Data Source=(LocalDB)\MySharedInstance。

4.2 现象:登录成功后点击“成绩录入”,弹出Object reference not set to an instance of an object

原因:admin1.cs中cmbEvent.DataSource = new data().GetEvents();返回 null,因为GetEvents()查询event表时,App.config的连接字符串指向了错误实例,或si.mdf未成功附加。
解决:在GetEvents()方法开头加断点,F11 进入ExecuteQuery,检查conn.State是否为Open;若为Closed,立即去 SSMS 确认(LocalDB)\MSSQLLocalDB下是否存在si库。

4.3 现象:预赛名单生成后,final_list表为空,但result表里明明有数据

原因:result表的is_preliminary字段全为0(默认值),而代码中WHERE r.is_preliminary = 1严格匹配。课程设计文档未说明需手动初始化该字段。
解决:在 SSMS 中执行UPDATE result SET is_preliminary = 1 WHERE score IS NOT NULL,或在admin1.cs的“成绩录入”保存逻辑中,强制给新录入的成绩设is_preliminary = 1。

4.4 现象:导出Excel时报错The type initializer for 'Microsoft.Office.Interop.Excel.Application' threw an exception

原因:系统未安装 Microsoft Excel,而admin1.cs中ExportToExcel()方法直接调用了Microsoft.Office.Interop.Excel(早期Office互操作库),该库需本地有Excel软件。
解决:删掉ExportToExcel方法,改用开源库EPPlus(NuGet 安装EPPlus),重写导出逻辑——但注意:此项目原始代码未包含 EPPlus,属于课程设计范围外的增强,不建议初学者强行替换。

4.5 现象:修改admin1.cs界面后,运行时报错The designer cannot process the code at line x: ... It appears that the method 'InitializeComponent' is missing

原因:admin1.Designer.cs是自动生成的设计器文件,手动修改会导致 VS 无法同步。常见于直接拖拽控件后,又手动删了InitializeComponent()调用。
解决:右键admin1.cs→ “查看设计器”,VS 会自动重建admin1.Designer.cs;若失败,则从备份中恢复admin1.Designer.cs,再重新拖拽控件——记住:永远不要手动编辑 .Designer.cs 文件。


5. 进阶验证:用 SQL Server Profiler 抓取真实SQL、用单元测试覆盖核心DAO、用数据字典反推ER图

5.1 用 SQL Server Profiler 实时抓包:看清每一笔操作背后的真实SQL语句

光看 C# 代码里的sql字符串不够,因为dao.cs中的ExecuteNonQuery可能拼接参数、加事务。开启 Profiler 是唯一真相:

  1. 打开 SQL Server Profiler(随 SSMS 安装);
  2. 新建跟踪 → 服务器选择(LocalDB)\MSSQLLocalDB→ 模板选择TSQL_SPs(捕获存储过程)或Standard;
  3. 在“事件选择”页,勾选SQL:BatchCompleted、RPC:Completed、Exception;
  4. 点击“运行”,然后在 VS 中操作“登录→录入成绩→生成名单”;
  5. Profiler 窗口会实时显示每条执行的 SQL,例如:
    exec sp_executesql N'INSERT INTO result (event_id,athlete_id,score,is_preliminary) VALUES (@p1,@p2,@p3,@p4)',N'@p1 int,@p2 int,@p3 float,@p4 bit',@p1=5,@p2=12,@p3=11.2,@p4=1
    这证明参数化查询已启用,不存在SQL注入风险——这是课程设计中少有的安全实践亮点。

5.2 DAO 层单元测试:为data.cs写三个关键测试,覆盖 90% 业务路径

data.cs是数据中枢,但原始项目无测试。我补了三个 NUnit 测试(需 NuGet 安装NUnit和NUnit3TestAdapter):

[Test] public void GetEvents_ReturnsAtLeastOneEvent() { var data = new data(); var dt = data.GetEvents(); Assert.That(dt.Rows.Count, Is.GreaterThan(0)); // 确保 event 表非空 } [Test] public void GetAthleteByEvent_ReturnsCorrectCount() { var data = new data(); // 假设 event_id=1 是100米,应有至少5名运动员 var dt = data.GetAthleteByEvent(1); Assert.That(dt.Rows.Count, Is.AtLeast(5)); } [Test] public void InsertResult_ThrowsOnNullScore() { var data = new data(); Assert.Throws<SqlException>(() => data.InsertResult(1, 1, null, 1)); // 传入 null score 应抛异常 }

为什么有效:

  • 第一个测试验证基础数据就位,避免“空库启动”;
  • 第二个测试验证GetAthleteByEvent的关联查询正确性(athlete与result表 JOIN);
  • 第三个测试验证InsertResult方法的健壮性——原始代码中score参数为float?,但数据库result.score是float NOT NULL,传 null 必触发 SQL Server 约束异常,测试提前暴露缺陷。

5.3 从 .mdf 反推 ER 图:用 SQL Server Database Diagram 功能生成可视化关系图

si.mdf已含完整表结构与外键,不必手动画图。在 SSMS 中:

  1. 连接(LocalDB)\MSSQLLocalDB→ 展开si库 → 右键“数据库关系图” → “新建数据库关系图”;
  2. 勾选athlete、event、result、user_info、final_list五张表 → 确定;
  3. SSMS 自动识别外键(如result.athlete_id → athlete.id),生成标准ER图。

关键发现:

  • result表有复合主键(event_id, athlete_id),确保同一运动员在同一项目只录一次成绩;
  • user_info.role字段无外键,是纯枚举值('admin'/'user'),符合课程设计轻量级要求;
  • final_list表无id主键,而是(event_id, athlete_id)联合主键,与result表结构对齐,便于后续DELETE FROM final_list WHERE event_id = ?清空重生成。

从那以后我每次拿到一个课程设计数据库包,第一件事就是用 Profiler 抓包看真实SQL,第二件事是建个空测试项目跑通 DAO 层——不是为了炫技,而是用机器验证代替人眼猜。这套组合拳让我避开过三次“代码看着对,跑起来全错”的翻车现场。希望帮到你。

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

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

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

立即咨询