基于成绩优先级、志愿顺序与座位余量的学生院校分配实现问题
一键分配院校功能实现方案
核心实现逻辑
- 提前批量加载所需全量数据到内存,避免循环查询数据库,大幅提升执行效率
- 维护院校实时剩余座位字典,分配成功后立即扣减对应院校座位数,保证后续匹配的准确性
- 严格按照分数从高到低的顺序处理学生,每个学生的志愿按
order_preference升序逐个校验 - 匹配到有剩余座位的志愿院校后,立即更新学生的分配院校字段,终止该学生的后续志愿校验;所有志愿均无空位则不为该学生分配院校
- 引入事务保证数据一致性,分配过程出现异常可全部回滚,避免出现部分数据更新的问题
优化后完整代码
using (SqlConnection dbcon = new SqlConnection(_conString)) { dbcon.Open(); // 开启事务,保证分配操作的原子性 using (SqlTransaction tran = dbcon.BeginTransaction()) { try { // 1. 查询所有成绩合格且按分数降序排列的学生 SqlCommand scmd = new SqlCommand( "SELECT stud_Id, stud_fname, stud_purcentage FROM tblStudent WHERE stud_result = 'Pass' ORDER BY stud_purcentage DESC", dbcon, tran); SqlDataAdapter da = new SqlDataAdapter(scmd); DataTable dtPassStudents = new DataTable(); da.Fill(dtPassStudents); // 2. 查询所有填报的志愿,按学生ID分组、按志愿优先级排序 SqlCommand cmdSelection = new SqlCommand( "SELECT tas.stud_id, tas.Acad_id, ta.seat_available FROM tblAcademy_Selection tas JOIN tblAcademy ta ON tas.Acad_id = ta.Acad_id ORDER BY tas.stud_id, tas.order_preference ASC", dbcon, tran); SqlDataAdapter daSelection = new SqlDataAdapter(cmdSelection); DataTable dtAllSelections = new DataTable(); daSelection.Fill(dtAllSelections); // 按学生ID分组存储志愿 var studentSelections = dtAllSelections.AsEnumerable() .GroupBy(r => r.Field<int>("stud_id")) .ToDictionary(g => g.Key, g => g.ToList()); // 3. 初始化院校剩余座位字典 SqlCommand cmdAcademy = new SqlCommand( "SELECT Acad_id, seat_available FROM tblAcademy", dbcon, tran); SqlDataAdapter daAcademy = new SqlDataAdapter(cmdAcademy); DataTable dtAcademy = new DataTable(); daAcademy.Fill(dtAcademy); var academySeat = dtAcademy.AsEnumerable() .ToDictionary(r => r.Field<int>("Acad_id"), r => r.Field<int>("seat_available")); // 4. 逐个处理学生分配 foreach (DataRow studentRow in dtPassStudents.Rows) { int studId = Convert.ToInt32(studentRow["stud_Id"]); int? assignedAcadId = null; // 该学生没有填报志愿,直接跳过 if (!studentSelections.ContainsKey(studId)) continue; // 按优先级遍历志愿 foreach (var selectionRow in studentSelections[studId]) { int acadId = selectionRow.Field<int>("Acad_id"); // 院校还有剩余座位 if (academySeat[acadId] > 0) { assignedAcadId = acadId; // 扣减对应院校剩余座位 academySeat[acadId]--; break; } } // 分配成功,更新学生表的acad_id字段 if (assignedAcadId.HasValue) { SqlCommand cmdUpdateStudent = new SqlCommand( "UPDATE tblStudent SET acad_id = @AcadId WHERE stud_Id = @StudId", dbcon, tran); cmdUpdateStudent.Parameters.AddWithValue("@AcadId", assignedAcadId.Value); cmdUpdateStudent.Parameters.AddWithValue("@StudId", studId); cmdUpdateStudent.ExecuteNonQuery(); } } // 5. 更新院校表的剩余座位数 foreach (var item in academySeat) { SqlCommand cmdUpdateAcademy = new SqlCommand( "UPDATE tblAcademy SET seat_available = @SeatCount WHERE Acad_id = @AcadId", dbcon, tran); cmdUpdateAcademy.Parameters.AddWithValue("@SeatCount", item.Value); cmdUpdateAcademy.Parameters.AddWithValue("@AcadId", item.Key); cmdUpdateAcademy.ExecuteNonQuery(); } // 所有操作成功,提交事务 tran.Commit(); } catch (Exception ex) { // 出现异常回滚所有操作 tran.Rollback(); // 可按需添加异常日志记录逻辑 throw ex; } } }
注意事项
- 代码使用参数化查询,避免了SQL注入风险
- 你的示例数据中,只有学生1(Alex)成绩合格且填报了志愿,会分配到第一志愿院校A,院校A的剩余座位从2变为1;其余合格学生没有填报志愿,不分配院校
- 若需要支持志愿为空的学生服从调剂,可在所有志愿匹配失败后增加遍历所有剩余空位院校的逻辑
内容的提问来源于stack exchange,提问作者DeathStriker2
相关产品推荐
相关产品推荐

