You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 08:15:42