C#中多函数返回数据表的高效合并优化方案咨询
性能优化请求:合并多数据表的函数耗时过长
我有一个函数,通过合并多个子函数返回的数据表生成最终结果,各子函数逻辑独立,且都以student_id作为共同主键。现在处理50名学生的数据时,这个函数耗时接近两分钟,希望得到最快的实现方案。
现有代码
主函数
public DataTable ShowReportOnGridivew(int class_id, string searchDate) { DataTable dt_students_List = null; try { //====主表=====// dt_students_List = GetDistinctStudentList(class_id);//至少会返回50名学生的数据 if (dt_students_List != null) dt_students_List.PrimaryKey = new DataColumn[] { dt_students_List.Columns["student_id"] }; //需要合并到主表的子表 DataTable dt_CurrentRank = null; DataTable dt_ScoreInEnglish = null; DataTable dt_AcademicDetails = null; DataTable dt_ExtraCurriculam = null; DataTable dt_Attendance = null; DataTable dt_Arts = null; DataTable dt_FuelToBridger = null; DataTable dt_FuelToAircraft = null; DataTable dt_TeacherFeedback = null; DataTable dt_TotalScore = null; foreach (DataRow row in dt_students_List.Rows) { string student_id = row["student_id"].ToString();//主键 //==========当前排名================// dt_CurrentRank = GetCurrentRank(student_id);//通过SQL获取数据 dt_CurrentRank.PrimaryKey = new DataColumn[] { dt_CurrentRank.Columns["student_id"] }; if (dt_CurrentRank != null) { dt_students_List.Merge(dt_CurrentRank); } //======英语成绩===== dt_ScoreInEnglish = GetScoreInEnglish(student_id, searchDate); dt_ScoreInEnglish.PrimaryKey = new DataColumn[] { dt_ScoreInEnglish.Columns["student_id"] }; if (dt_ScoreInEnglish != null) { dt_students_List.Merge(dt_ScoreInEnglish); } //======学业详情===== dt_AcademicDetails= GetAcademicDetails(student_id); dt_AcademicDetails.PrimaryKey = new DataColumn[] { dt_AcademicDetails.Columns["student_id"] }; if (dt_AcademicDetails != null) { dt_students_List.Merge(dt_AcademicDetails); } //=====其余子函数调用逻辑类似,合并对应列到主表====== } } catch (Exception show_error) { string log_data = "Response: Error- " + show_error.ToString(); obj.DatalogFile("StudentsList", log_data); throw show_error; } return dt_students_List; }
子函数示例(GetCurrentRank)
private async DataTable GetCurrentRank(string student_id) { DataTable dt = null; SqlConnection con = new SqlConnection(conn); SqlCommand cmd = null; SqlDataReader dr = null; string sql = string.Empty; sql = "SELECT student_id,current_rank FROM student_details WHERE " + " student_id = @student_id "; cmd = new SqlCommand(sql, con); con.Open(); try { cmd.CommandType = CommandType.Text; cmd.Parameters.AddWithValue("@student_id", student_id); dr = cmd.ExecuteReader(); dt = new DataTable("CurrentRank"); dt.Columns.AddRange(new DataColumn[2] { new DataColumn("student_id", typeof(int)), new DataColumn("current_rank", typeof(float))}); if (dr.HasRows) { dt.Load(dr); } string log_data = "Web App Function: GetCurrentRank \n"; log_data += "Response: " + JsonConvert.SerializeObject(dt); obj.DatalogFile("GetCurrentRank", log_data); return dt; } catch (Exception show_error) { string log_data = "Response: Error- " + show_error.ToString(); obj.DatalogFile("GetCurrentRank", log_data); throw show_error; } finally { cmd.Dispose(); con.Close(); con.Dispose(); } }
最优实现方案
1. 批量查询替代单条查询(核心优化)
当前代码最大的性能瓶颈是循环执行单条SQL查询,50个学生×10个子函数=500次数据库请求,每次请求的连接、执行、断开开销极大。改为一次性批量查询所有学生的对应数据:
- 把
student_id列表作为参数传入子函数,使用**表值参数(TVP)**查询(比IN子句更高效,支持大量ID)。 - 修改后的
GetCurrentRank批量查询示例:
private DataTable GetCurrentRank(List<int> studentIds) { DataTable dt = new DataTable("CurrentRank"); dt.Columns.AddRange(new DataColumn[2] { new DataColumn("student_id", typeof(int)), new DataColumn("current_rank", typeof(float)) }); using (SqlConnection con = new SqlConnection(conn)) { // 创建表值参数 DataTable tvp = new DataTable(); tvp.Columns.Add("student_id", typeof(int)); foreach (int id in studentIds) { tvp.Rows.Add(id); } string sql = @"SELECT s.student_id, s.current_rank FROM student_details s INNER JOIN @StudentIds tvp ON s.student_id = tvp.student_id"; using (SqlCommand cmd = new SqlCommand(sql, con)) { cmd.Parameters.Add(new SqlParameter("@StudentIds", SqlDbType.Structured) { TypeName = "dbo.StudentIdList", // 需提前在SQL Server创建对应表类型 Value = tvp }); con.Open(); using (SqlDataReader dr = cmd.ExecuteReader()) { if (dr.HasRows) { dt.Load(dr); } } } } return dt; }
- 提前在SQL Server创建表类型:
CREATE TYPE dbo.StudentIdList AS TABLE (student_id INT)
2. 合并逻辑优化:批量合并而非逐行合并
获取所有子表的批量数据后,直接一次性合并到主表,无需在循环里逐行操作:
// 提取主表中所有学生ID List<int> studentIds = dt_students_List.AsEnumerable() .Select(r => r.Field<int>("student_id")) .ToList(); // 批量获取所有子表数据 DataTable dt_CurrentRank = GetCurrentRank(studentIds); DataTable dt_ScoreInEnglish = GetScoreInEnglish(studentIds, searchDate); // ...其余子函数批量调用 // 一次性合并所有子表到主表 dt_students_List.Merge(dt_CurrentRank); dt_students_List.Merge(dt_ScoreInEnglish); // ...其余合并操作
3. 数据库连接复用
子函数改用using语句自动管理连接,利用ADO.NET的连接池特性,避免重复创建销毁连接的开销(示例已包含在批量查询代码中)。
4. 异步并行调用子函数
如果子函数之间没有依赖,可并行调用批量查询的子函数,进一步减少等待时间:
// 主函数需改为async Task<DataTable>类型 var tasks = new List<Task<DataTable>>(); tasks.Add(GetCurrentRankAsync(studentIds)); tasks.Add(GetScoreInEnglishAsync(studentIds, searchDate)); // ...添加其余子函数的异步任务 await Task.WhenAll(tasks); // 获取结果并合并 dt_students_List.Merge(tasks[0].Result); dt_students_List.Merge(tasks[1].Result); // ...其余合并操作
5. 减少不必要的日志开销
当前子函数将DataTable序列化为JSON写入日志,批量数据时会产生额外性能损耗,建议只记录关键信息(如查询学生数量、是否成功),而非完整数据。
6. 数据库层面优化
- 确保
student_id等关联字段都创建了索引,加速查询。 - 若业务允许,将多个子查询合并为一个复杂SQL,一次性获取所有需要的数据,彻底减少数据库请求次数。
内容的提问来源于stack exchange,提问作者nsds
相关产品推荐
相关产品推荐

