ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

运动会管理系统数据库设计:五张表搞定成绩入库与总分统计

运动会管理系统数据库设计:五张表搞定成绩入库与总分统计 简介针对高校运动会管理系统的数据库课程设计文档面向计算机相关专业需完成数据库课程设计的在校生提供从需求分析到实施维护的完整设计思路。文档围绕赛前准备、赛中管理、赛后处理三大功能模块详细展开需求功能分析、业务需求分析、系统权限设计、数据流程图并完成概念设计中的实体联系、E-R图、关系模式图以及逻辑设计中的关系模式转化和学校表、部门表、老师表、学生表、比赛项目表、赛程安排表、比赛结果表等数据表定义最后还涉及物理结构设计、数据库备份恢复与性能优化。资源为单个doc文件大小187KB内容层次清楚可帮助读者快速掌握运动会管理系统数据库设计的流程与方法也可作为课程设计报告撰写或项目开发的直接参考资料。该资源已有175人学习。1. 运动会管理系统到底考什么不是界面是成绩怎么进库高校运动会管理系统这样的数据库课程设计题目第一眼很像“系统开发题”要报名页面、运动员管理、成绩打印……但做完一遍你会发现界面只是外衣真正的分水岭全在数据库设计上。一个运动会的最小业务链只有三条报名、录成绩、算团体总分。这三步都围绕同一批数据在流转表怎么拆、字段怎么定、约束怎么加直接决定你能不能在一周内把系统做完而不是在答辩前夜还在改表结构。这套设计适合两类人一是正在做数据库课程设计的学生想找一个能完整讲清 ER 图、范式、事务的题目二是想拿运动会当练手、为 Spring Boot 或 Vue 后台管理系统打底的人。本文给出的方案以 MySQL 5.7/8.0 为例表结构可以迁移到 SQL Server 或 PostgreSQL。先把表设计立住后面所有功能都是增删改查的排列组合。2. 核心表设计五张表把运动会数据模型拆干净运动会管理系统要管理的实体不难列系部、运动员、比赛项目、成绩。难的是“报名”和“成绩”这两条关系。一个运动员可以报多个项目一个项目有多个运动员这是典型多对多需要报名表承接成绩既属于某个运动员或某个系部又挂在某个项目下还需要保留轮次和名次信息。后面所有排名、加分、破纪录统计都从这张成绩表往外算。我见过的课程设计翻车现场大多不是界面写不出来而是表里少了字段或者多拆了表。少字段的典型是成绩表只存一个分数结果团体项目没法录多拆表的典型是预赛一张表、决赛一张表最后总分统计要写三段拼接 SQL。2.1 建表顺序与关系外键约束下的五张表先给结论这个系统五张表就够了department系部、athlete运动员、event比赛项目、enrollment报名、result成绩。为什么说四张“核心”因为报名表是关系表本身不承载业务实体但它决定多对多能不能查清楚。建表顺序要顺着外键走先建 department 和 event再建 athlete依赖 department再建 enrollment依赖 athlete 和 event最后建 result依赖 event、department成绩和运动员用可空外键关联。如果顺序反了MySQL 会报“无法创建外键约束”的错这不是 MySQL 苛刻而是它帮你提前发现了模型里不存在的依赖关系。这个顺序也对应着你画 ER 图的思路实体先定关系后定关系的属性报名时间、分组号最后补。课程设计报告里一般要附 ER 图画的时候把 enrollment 和 result 标成关系的转化表而不是画成独立实体答辩时好讲。2.2 运动员与项目之间的多对多报名表的设计取舍运动员和比赛项目是多对多所以报名表必须存在。这张表最核心的设计不是加 id 主键而是加一个唯一约束(athlete_id, event_id)。没有这个约束同一个运动员同一项目能插入两条记录程序里再怎么做防重都是后置拦截数据库层面直接挡住才是可靠的。团体项目是个容易绕晕的地方。团体项目报名的实体不是运动员而是系部比如 4×100 接力一个系报一队。常见坏做法是把报名主体硬塞给 athlete 表用一个“集体”运动员代表全系成绩算完还要在程序里特判。我一般会这样处理报名表只承接个人项目团体项目不建报名表直接由裁判在 result 表按 department_id 录入最终成绩。这样个人赛和团体赛在总分统计时走同一条查询路径只是团体赛的积分翻倍而已。你可能会问那团体赛的参赛名单不需要存吗课程设计层面不需要。如果你的需求文档明确要求管理团体赛名单那就再加一张 team_enrollmentteam_id, event_id, athlete_id但总分统计仍然只看 result 表不要把名单表和成绩表混在一起。2.3 成绩表核心设计存名次而非积分result 表是整套系统最容易设计错的地方。很多人的第一设计是成绩表里存“运动员、项目、得分”甚至直接把 7 分、5 分算好存进去。这个设计表面上省了程序里的换算实际上是把规则和数据库耦合了。今天第一名 7 分明天学校改成第一名 9 分你得 UPDATE 整张历史成绩表还可能误伤往届数据。正确做法是成绩表只存原始事实跑出的时间、跳出的米数、本次名次积分是算出来的用一条 CASE WHEN 或一张积分规则表去映射。这就是把“名次”作为事实把“积分”作为派生数据。派生数据不进表要查的时候现算。对于课程设计来说这个设计思想本身就是答辩加分点老师问“如果规则改了你怎么办”你能答出来“改映射不动成绩表”。另一个关键是轮次字段。一场运动会的小组赛、预赛、决赛同一个运动员在同一个项目可能出现多次。如果在结果表上把成绩存成列预赛成绩一列、决赛成绩一列那张表就废了因为你没法知道一个项目到底有几个轮次。正确设计是加 round_no预赛存 1、决赛存 2名次和成绩都带轮次总分统计时按“只取决赛轮”过滤。2.4 完整建表 SQL字段类型、索引与字符集下面这套 SQL 可以直接在 MySQL 5.7/8.0 里跑通。字符集统一用 utf8mb4原因后面避坑章会讲这里先记住建库时就指定不要依赖默认值。CREATE DATABASE IF NOT EXISTS sports_meeting DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE sports_meeting; CREATE TABLE department ( id INT AUTO_INCREMENT PRIMARY KEY, dept_name VARCHAR(50) NOT NULL COMMENT 系部名称 ) ENGINEInnoDB; CREATE TABLE athlete ( id INT AUTO_INCREMENT PRIMARY KEY, student_no VARCHAR(20) COMMENT 学号, name VARCHAR(30) NOT NULL COMMENT 姓名, sex ENUM(男,女) NOT NULL, department_id INT NOT NULL, KEY idx_dept (department_id), CONSTRAINT fk_athlete_dept FOREIGN KEY (department_id) REFERENCES department(id) ) ENGINEInnoDB; CREATE TABLE event ( id INT AUTO_INCREMENT PRIMARY KEY, event_name VARCHAR(50) NOT NULL COMMENT 项目名称, is_team TINYINT(1) NOT NULL DEFAULT 0 COMMENT 1团体 0个人, category ENUM(track,field) NOT NULL COMMENT 田赛或径赛 ) ENGINEInnoDB; CREATE TABLE enrollment ( id INT AUTO_INCREMENT PRIMARY KEY, athlete_id INT NOT NULL, event_id INT NOT NULL, group_no TINYINT DEFAULT 1 COMMENT 预赛分组号无分组填1, UNIQUE KEY uk_athlete_event (athlete_id, event_id), CONSTRAINT fk_enroll_athlete FOREIGN KEY (athlete_id) REFERENCES athlete(id), CONSTRAINT fk_enroll_event FOREIGN KEY (event_id) REFERENCES event(id) ) ENGINEInnoDB; CREATE TABLE result ( id INT AUTO_INCREMENT PRIMARY KEY, event_id INT NOT NULL, athlete_id INT NULL COMMENT 团体项目为空, department_id INT NOT NULL COMMENT 成绩归属系部, round_no TINYINT NOT NULL DEFAULT 1 COMMENT 轮次, raw_value VARCHAR(20) COMMENT 原始成绩文本, rank_no TINYINT NOT NULL COMMENT 本轮名次, UNIQUE KEY uk_event_round_rank (event_id, round_no, rank_no), CONSTRAINT fk_result_event FOREIGN KEY (event_id) REFERENCES event(id), CONSTRAINT fk_result_dept FOREIGN KEY (department_id) REFERENCES department(id) ) ENGINEInnoDB;几个字段选择说一下。athlete_id 在 result 表里允许为 NULL是因为团体项目没有对应的运动员成绩挂在系部上这是有意为之不是设计疏漏。department_id 在 result 表里必须非空因为个人赛要统计到系团体赛也要统计到系统一用这一个字段关联系部。uk_event_round_rank 这个唯一约束是防止“同一轮次同一项目出现两个第一名”如果你没有这个约束后面总分就会翻倍。event 表里 category 用的是 ENUMMySQL 对 ENUM 的合法性校验比字符串强插一个“swim”进去会被直接拒掉。is_team 用 TINYINT(1) 是给后面积分翻倍用的。raw_value 用 VARCHAR 存文本是为了兼容田赛“5米20”和径赛“12.35秒”两种格式注意它只是给人看的排名不按它排序排名由裁判手录的 rank_no 决定。3. 增删改查到总分榜三条 SQL 打通核心闭环表结构定完功能就是增删改查。运动会系统的管理端说白了四个入口运动员信息维护、项目报名、成绩录入、总分榜查看。这章把四条链路压成三条 SQL 来讲报名怎么防重、成绩怎么录、总分怎么算。每一条都对应你报告里“核心功能模块”那一章代码可以少但能讲清原理的代码值得留着。这章的演示端我选择 Python 3 PyMySQL因为它比 Java 准备环境快也比 JDBC 更直观。你不用把整个界面做出来先跑通连接、跑通查询再往上面套 Flask 或 PyQt 都行。3.1 报名唯一约束怎么拦重复报名动作是往 enrollment 表插记录。常见实现是先 SELECT 查一遍有没有重复没有才 INSERT。这个逻辑没问题但它把防重放在了应用层两个人同时提交同一个运动员的同一个项目时两条 SELECT 都返回空两条 INSERT 都成功。数据库层防重靠的是唯一约束 uk_athlete_event。有它在第二条 INSERT 会直接报 Duplicate entry 错误。应用层捕获这个错误给用户弹“已报过该项目”的提示。-- 给运动员 1001 报 3 号项目 INSERT INTO enrollment (athlete_id, event_id, group_no) VALUES (1001, 3, 1);再执行一次就能看到 1062 错误这就是约束在工作。如果你想连错误提示都省了可以用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE但课程设计里我不推荐因为你要让老师看到你是怎么发现重复的。INSERT IGNORE 适合批量导入报名表时静默去重单条报名用标准 INSERT 加异常处理就足够。group_no 这个字段要注意一个运动员报了 100 米预赛在第一组group_no 存 1如果同一项目有预赛和决赛决赛轮他不应该再出现在报名表里报名只报一次轮次是编排系统的事。课程设计不用做到编排分组自动化手动填组号即可但表里要有这个字段否则报告里“分组管理”功能无从谈起。3.2 成绩录入原子性从一条 UPDATE 开始成绩录入的特性和普通增删改查不一样它是一次修改不是一次插入。因为报名时运动员已经在系统里了录成绩应该是对 result 表做 UPDATE 或 UPSERT而不是每次都新插一条。评判员录入界面的交互通常是这样的选择项目与轮次列出该项目的参赛名单每个参赛者旁边填成绩和名次点保存。保存动作就是一组 UPDATEUPDATE result SET raw_value 12.35秒, rank_no 1 WHERE event_id 3 AND round_no 2 AND athlete_id 1001;这条 SQL 的问题在于 result 表里可能还没有 1001 这条记录。你当然可以要求先 INIT 好所有参赛者的 result 空记录也可以把 UPDATE 换成 INSERT ... ON DUPLICATE KEY UPDATE。我更推荐后者第一次录就是插入改错了再录就是更新一条语句覆盖两种状态。INSERT INTO result (event_id, athlete_id, department_id, round_no, raw_value, rank_no) VALUES (3, 1001, 2, 2, 12.35秒, 1) ON DUPLICATE KEY UPDATE raw_value 12.35秒, rank_no 1;ON DUPLICATE KEY UPDATE 触发的条件是 uk_event_round_rank 与已有记录冲突。这里有个隐藏逻辑一个项目的决赛轮只能有一个第一名如果已经有人占了第一名你再录 1001 为第一名会先触发 uk_event_round_rank 的冲突。这说明名次是全局唯一资源写代码时要把这个冲突理解成“有人已经录过这个名次了”而不是“这条成绩重复了”。程序里要提示裁判先核实别直接覆盖。3.3 团体总分一条 JOIN 加 GROUP BY 算榜团体总分是运动会系统的门面功能也是你报告里最好写“核心算法”的地方。它的本质不复杂把 result 表按 department_id 分组按名次映射积分后求和团体项目积分翻倍最后按总分降序排列。映射关系用 CASE WHEN 写在 SQL 里。下面这条 SQL 是整套系统的重点答辩时老师大概率会问这里的逻辑建议把每一段翻译成普通话说清楚。SELECT d.dept_name, SUM( CASE r.rank_no WHEN 1 THEN IF(e.is_team 1, 14, 7) WHEN 2 THEN IF(e.is_team 1, 10, 5) WHEN 3 THEN IF(e.is_team 1, 8, 4) WHEN 4 THEN IF(e.is_team 1, 6, 3) WHEN 5 THEN IF(e.is_team 1, 4, 2) WHEN 6 THEN IF(e.is_team 1, 2, 1) ELSE 0 END ) AS total_score, COUNT(DISTINCT r.event_id) AS medal_events FROM result r JOIN department d ON r.department_id d.id JOIN event e ON r.event_id e.id WHERE r.round_no ( SELECT MAX(round_no) FROM result r2 WHERE r2.event_id r.event_id ) GROUP BY d.id, d.dept_name ORDER BY total_score DESC;说几个容易看懵的地方。IF(e.is_team 1, 14, 7) 的意思是同一名次团体项目积分是个人项目的两倍。CASE 管名次IF 管单双倍两个嵌套在一起算单个名次的积分。WHERE 子句里的子查询是为了“只取每个项目的最后轮次”这样预赛成绩不会混进总分榜如果你不做预赛只有一轮这个过滤条件不影响结果。COUNT(DISTINCT r.event_id) 是顺便统计夺牌项目数破纪录并列时能多个参考指标。这条 SQL 的性能在小数据量下没有任何问题。几百条成绩记录MySQL 扫描一次 result 表也就几毫秒。需要注意的是别为了“看起来专业”去写窗口函数 ROW_NUMBER 取轮次MySQL 5.7 不支持窗口函数写出来在自己的机器上跑会报语法错误。当然如果你用的是 MySQL 8.0可以试用 RANK() 做并列名次但主讲方案仍是这条兼容 5.7 的写法。3.4 最小的 Python 演示端连通性先跑通做管理系统界面可以简单但数据库连通性演示必须有。Python 端最稳妥的组合是 PyMySQL 简单的命令行循环先把 SQL 跑通再考虑 Flask。下面是一段可以直接运行的查询脚本演示的就是 3.3 的总分榜。import pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, password123456, databasesports_meeting, charsetutf8mb4, autocommitFalse, ) sql SELECT d.dept_name, SUM(CASE r.rank_no WHEN 1 THEN IF(e.is_team 1, 14, 7) WHEN 2 THEN IF(e.is_team 1, 10, 5) ELSE 0 END) AS total_score FROM result r JOIN department d ON r.department_id d.id JOIN event e ON r.event_id e.id WHERE r.round_no (SELECT MAX(round_no) FROM result r2 WHERE r2.event_id r.event_id) GROUP BY d.id, d.dept_name ORDER BY total_score DESC try: with conn.cursor() as cursor: cursor.execute(sql) for row in cursor.fetchall(): print(row) conn.commit() finally: conn.close()命令行跑完后你会看到一行行 (系部名, 总分)。连接参数里两个最值得注意的autocommit 设成 False是因为后面要讲事务时得有一个“不自动提交”的会话charset 设成 utf8mb4少一个字符集就多一个乱码事故见第四章第 5 节。如果连不上先检查 MySQL 服务是否启动再检查 root 密码是否正确最后看 3306 端口有没有被占用。课程设计阶段大多数连接报错都出在这三处顺序排查即可。4. 常见问题与避坑这些坑越早踩越好这一章写的是我在运动会系统这类课程设计里反复见到的真问题。每一类都有明确的现象、原因和解决路径。你不需要背下来但建议把自己的数据库设计对照着一项项检查中一条改一条。提前踩坑比答辩时被老师当面指出要体面得多。4.1 名次重复导致积分翻倍唯一约束到底加在哪现象总分榜算出来比现场广播的分数多了一大截。查成绩表发现同一个项目同一个轮次有两个第一名。原因result 表只对 id 做主键没有对 (event_id, round_no, rank_no) 加唯一约束。录入员把已经录过的成绩又录了一遍或者两名裁判录了不同运动员、相同名次。程序按名次映射积分后第一名被算了两次。解决给 result 表加联合唯一约束 uk_event_round_rank。加约束之前先清理重复数据否则 ALTER TABLE 会失败。清理方式按实际情况选择DELETE 掉多余记录或者把其中一条的 rank_no 改成正确名次。这个约束的代价是“任何一轮次的名次只能出现一次”对运动会来说这正是事实——一个名次不可能同时颁给两个人除非是并列并列时应该用 tie_rank 字段处理而不是直接插同数字。4.2 时间成绩用 VARCHAR 存导致排序全乱现象100 米成绩排序12.4 秒排到了 9.8 秒后面因为字符串按字典序比较9.8 比 1 大。原因建表时为了图省事把成绩全存成 VARCHAR包括径赛的时间。VARCHAR 存“12.35秒”这样的文本没问题但它不能参与数值比较。解决成绩要分类型处理。径赛成绩建议存 DECIMAL(5,2)单位是秒9.8 存成 9.80田赛的米数、厘米数同理。非要显示成“12.35秒”的由前端在展示时拼接单位。如果需求里必须保留原始文本那就分成两列raw_value 存给人看的文本score_value 存给机器排名的数值。第二章的建表 SQL 里我只留了 raw_value那是因为课程设计简化处理、排名靠手录名次如果要支持“按成绩自动生成名次”就必须把 score_value 补上。4.3 小组赛和决赛拆成两张表总分统计成拼接地狱现象一个 100 米项目预赛三组、决赛一组表设计成了 result_preliminary 和 result_final 两张。总分榜要先查决赛表算分预赛破纪录的成绩还要再查一遍预赛表补加代码逻辑越长越没人能维护。原因设计时把“轮次”理解成了“不同表”而不是同一张表里的一个属性。解决轮次是成绩的维度不是表。一张 result 表加 round_no预赛存 1决赛存 2所有统计都基于“取该项目最大轮次”这一条规则。之前 3.3 的 SQL 里特意加了 WHERE round_no 最大轮次的过滤就是给这个场景兜底。如果分层赛的规则更复杂比如甲乙组分开也应该是加 group_no 或 category而不是再拆表。4.4 DELETE 忘加 WHERE演示现场成绩全没了现象演示时想在成绩表里删掉一条误录记录执行了 DELETE FROM result回车之后才发现没写 WHERE几千条成绩瞬间没了。MySQL 默认没有回收站没有开启事务的话连后悔药都没有。原因严格说这不是设计问题是操作习惯问题。图形化工具里 DELETE 没有二次确认命令行里 DELETE 不加 WHERE 更是零提示。解决三个办法叠一起才稳妥。第一给 result 表所有写入操作包事务误删就 ROLLBACK。第二每次 DELETE 先写 SELECT 确认范围确认行数再删。第三对成绩表这种业务数据课程设计阶段可以用“作废字段”代替删除加一个 voided TINYINT 字段误操作了把 voided 置 1统计时过滤掉而不是直接 DELETE。这个设计在真实系统里很常见写在报告里还能展示你对数据安全的考虑。4.5 中文字符串全部变成问号现象运动员姓名、系部名称在网页或 Python 里读出来全是??数据库里存进去的时候就已经是问号了。原因建库时没指定默认字符集MySQL 用的可能是 latin1 或默认的 latin1_swedish_ci。表虽然是中文数据存储时被转成了问号已经是不可逆损坏。连接时没有指定 charset 也会出现类似问题但那是临时的连接参数改对就行。解决从建库这一步就锁死字符集。建库语句用 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci连接参数带上 charsetutf8mb4。需要特别注意如果数据库已经建好了ALTER DATABASE 改字符集只影响新表已有的损坏数据还是坏的只能重新导入。Navicat 导入 Excel 报名表时也要在导入向导里选 UTF-8别选默认的 ANSI。5. 答辩前必调的细节索引、事务与演示边界系统跑通只是第一步答辩时老师问的问题往往不看你界面多好看而是看你对自己软件的理解。这章讲三个硬件层面之外的技术点索引怎么建、事务往哪放、连接池要不要上最后一节给一份答辩前晚上的检查清单。这些调整不会让你的程序看起来多五十行代码但能让你的系统经得住盘问。5.1 联合索引与 EXPLAIN不是有索引就不慢报名表上我们建了 UNIQUE KEY uk_athlete_eventathlete_id, event_id。这个索引既能防重复又能加速“按运动员查项目”。但注意它加速不了“按项目查运动员”因为联合索引的最左前缀是 athlete_id你在 WHERE 里只给 event_id 时这个索引用不上。EXPLAIN SELECT * FROM enrollment WHERE event_id 3;跑一下这条 EXPLAIN看 type 列是 ALL 就是全表扫描。解决方法是再补一个 KEY idx_event_id (event_id)或者把索引顺序反过来建 (event_id, athlete_id)。两者都建会占空间但运动会系统这种数据量完全无所谓。真正要理解的是联合索引的字段顺序决定了它能服务哪些查询。这门课考试可能不考但老师问你“这个表有哪些索引、分别为什么而建”时你能答出这条链路比背概念强得多。5.2 事务放哪成绩录入的回滚粒度事务在课程设计里最常见的误用是到处 START TRANSACTION仿佛不用事务就显得不专业。事务的价值在于原子性也就是说一个业务动作要么全部完成、要么全部不完成。运动会系统里真正需要事务的是“批量成绩录入”这个动作。比如裁判录入一组八条成绩录到第五条发现第四条名次错了如果每条 UPDATE 都自动提交那前四条已经写进库了得手工再 UPDATE 一遍。正确做法是把一轮的成绩录入包进一个事务START TRANSACTION; UPDATE result SET raw_value12.35, rank_no1 WHERE event_id3 AND round_no2 AND athlete_id1001; UPDATE result SET raw_value12.50, rank_no2 WHERE event_id3 AND round_no2 AND athlete_id1002; -- 发现 rank_no 顺序录反了回滚重录 ROLLBACK; -- 或者全部无误后提交 COMMIT;这段代码演示的重点不在 SQL 本身而在“为什么不逐条提交”。选手成绩一旦出现在公示榜上就很难撤回事务给了录入员一次整体重来的机会。隔离级别在课程设计里不需要修改MySQL 默认的 REPEATABLE READ 够用你只需要在报告里写“成绩录入采用事务保证原子性”这句话本身就是一个答辩加分项。5.3 连接池要不要上先讲清它解决什么打开招聘要求后台管理系统十个里有八个写“熟悉数据库连接池”。于是很多课程设计也在 JDBC 里引入 C3P0 或 Druid配一堆 XML 然后把连接拿过来用。这里我建议分情况如果你的系统只是单机演示、十几个用户同时操作连接池带来的复杂度和风险远高于收益但如果你的设计文档里写了“供多家系部同时录入成绩”那就应该在架构上考虑连接池。连接池解决的是连接重复建立的开销。传统 JDBC 每执行一次 SQL 就要 TCP 握手、三次认证高并发下建立连接的时间可能比执行 SQL 还长。连接池把连接提前建好放在池子里用的时候借、用完还省掉握手开销。这个原理比代码更重要答辩时老师大概率会问“你用的连接池做了什么”你答得出“复用连接、减少握手”就过关了。配套的一种小规模实现是不需要引入第三方库自己写一个静态的连接持有者项目启动时建立一条连接整个生命周期复用。数据量几千条、并发个位数的场景这比连接池实际得多。你可以在报告里写“本系统规模未达到连接池阈值采用单连接复用方案”这比强行上一套连接池然后讲不出原理要诚实得多。5.4 演示数据与边界答辩前最后一晚检查清单答辩演示的翻车往往不在功能缺失而在数据边界。以下是我每次演示前必查的四项一是空数据演示。把数据库清空后从报名到出总分榜完整走一遍确保没有 NPE 或 MySQL 返回空集时程序不死。很多系统在“有数据”时一切正常一清库就暴露问题。二是同名运动员。两个叫“张伟”的运动员报名了不同项目成绩表要能区分。演示时用姓名演示查询就会撞车要带上学号或 ID 来展示查询。三是名次并列。实际运动会经常出现并列第三你的成绩表如果强制名次唯一就要能解释并列怎么处理。课程设计通常简化为不处理并列但你至少要提前想好话术“并列时以裁判组裁决为准系统按最终名次录入。”四是删除防线。前面 4.4 提过的事务和 SELECT 先确认在演示当天再默写一遍。紧张环境下最容易手快多一道防线就多一分从容。这份清单不是写给你抄的而是让你基于自己的系统再过一遍。课程设计和真实项目的差距往往就是这些边界条件处理的差距。把这些查完你演示时出的岔子会少掉六成。6. 从课程设计走向完整系统先保表结构再换技术栈运动会管理系统做完了很多人下一步想的是把它升级成 Spring Boot Vue 的完整前后端项目或者换到 SQL Server 上。换框架这件事我的原则是表结构一对一带过去其他都好说。先把五张表原样迁移department 的 INT 主键、athlete 的 ENUM 性别、enrollment 的唯一约束、result 的联合唯一约束全都是标准 SQL 的通用写法。MySQL 迁移到 PostgreSQL 时把 ENUM 换成 VARCHAR 加 CHECK 约束TINYINT 换成 SMALLINT建表顺序保持外键优先导入导出用 Navicat 的“转储 SQL 文件”即可。SQLite 不适合做这个系统并发和约束支持都太弱课程设计里避开。技术栈的替换顺序我应该早点说界面技术从控制台版换成 Flask 或者 Vue只动表现层存储过程、视图和索引这些数据库资产不要重写而是原样保留。MySQL 连接池、ORM 映射MyBatis 或 SQLAlchemy都是在表结构之上的封装表结构不稳换什么框架都是白搭。我最初做运动会系统时犯过最大的错就是把团体项目挂在运动员表上为了让接力赛能录成绩硬造出一个“0号运动员”。结果总分统计时一路特判代码写到最后自己都看不懂。后来推翻重来把成绩归属改成系部字段一张 result 表通吃所有项目整个项目瞬间清爽了。那次之后我养成了一个习惯先拿表设计说服自己再动界面。这套五表结构经过多年验证希望也能帮到你。本文还有配套的精品资源点击获取
返回列表