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

DataTable数据重排性能优化求助:替代嵌套循环提速方案

问题描述

将Excel文件导入DataTable(dtImport)后,需要将其中数据重排至另一个DataTable(dtImportParsed)。当前通过嵌套for循环实现重排逻辑,但处理36列、4000行的表格需耗时30-40分钟,急需提升处理速度的替代方案。

现有代码如下:

for (int c = 2; c < dtImport.Columns.Count; c++) //for each date column
{
    for (int r = 1; r < dtImport.Rows.Count; r++)
    {
        if (dtImportParsed.Rows.Count == 0)
        {
            DataRow dataRowImport = dtImportParsed.NewRow();
            dataRowImport["Date"] = dtImport.Columns[c].ColumnName.ToString().Trim();
            dataRowImport["account_id"] = dtImport.Rows[r]["account_id"].ToString().Trim();
            dataRowImport[dtImport.Rows[r]["Event Name"].ToString().Trim()] = dtImport.Rows[r][c].ToString().Trim();
            dtImportParsed.Rows.Add(dataRowImport);
        }
        else
        {
            for (int i = 0; i < dtImportParsed.Rows.Count; i++)
            {
                if (dtImportParsed.Rows[i]["account_id"].ToString() == dtImport.Rows[r]["account_id"].ToString())
                {
                    if (dtImportParsed.Rows[i]["Date"].ToString() == dtImport.Columns[c].ColumnName.ToString())
                    {
                        dtImportParsed.Rows[i][dtImport.Rows[r]["Event Name"].ToString().Trim()] = dtImport.Rows[r][c].ToString().Trim();
                        break;
                    }
                }
                else if (i == dtImportParsed.Rows.Count - 1)
                {
                    DataRow dataRowImport = dtImportParsed.NewRow();
                    dataRowImport["Date"] = dtImport.Columns[c].ColumnName.ToString().Trim();
                    dataRowImport["account_id"] = dtImport.Rows[r]["account_id"].ToString().Trim();
                    dataRowImport[dtImport.Rows[r]["Event Name"].ToString().Trim()] = dtImport.Rows[r][c].ToString().Trim();
                    dtImportParsed.Rows.Add(dataRowImport);
                }
            }
        }

    }
}
性能瓶颈分析

现有代码采用三层嵌套循环:外层遍历日期列,中层遍历数据行,内层遍历目标表的所有行进行匹配查找。随着目标表行数增加,内层循环的遍历成本呈线性增长,最终导致整体时间复杂度达到O(MNK)(M为日期列数,N为源表行数,K为目标表行数),处理大量数据时效率极低。

优化方案:使用字典实现O(1)查找

通过创建一个Dictionary,以(account_id, Date)的组合作为唯一键,对应目标表中的DataRow,这样每次匹配查找的时间复杂度降为O(1),整体时间复杂度可优化至O(M*N),能大幅提升处理速度。

优化后的代码如下:

// 定义字典,键为(account_id, Date)的元组,值为对应的DataRow
var rowLookup = new Dictionary<(string AccountId, string Date), DataRow>();

// 遍历所有日期列(从第3列开始,索引2)
for (int c = 2; c < dtImport.Columns.Count; c++)
{
    string currentDate = dtImport.Columns[c].ColumnName.Trim();
    
    // 遍历所有数据行(从第2行开始,索引1)
    for (int r = 1; r < dtImport.Rows.Count; r++)
    {
        DataRow sourceRow = dtImport.Rows[r];
        string accountId = sourceRow["account_id"].ToString().Trim();
        string eventName = sourceRow["Event Name"].ToString().Trim();
        string cellValue = sourceRow[c].ToString().Trim();

        // 构造查找键
        var lookupKey = (AccountId: accountId, Date: currentDate);
        
        if (rowLookup.TryGetValue(lookupKey, out DataRow targetRow))
        {
            // 找到已有行,直接赋值对应事件列
            targetRow[eventName] = cellValue;
        }
        else
        {
            // 未找到,创建新行并加入字典和目标表
            targetRow = dtImportParsed.NewRow();
            targetRow["Date"] = currentDate;
            targetRow["account_id"] = accountId;
            targetRow[eventName] = cellValue;
            
            dtImportParsed.Rows.Add(targetRow);
            rowLookup.Add(lookupKey, targetRow);
        }
    }
}
额外优化建议
  1. 提前缓存列索引:如果源表的account_id和Event Name列位置固定,可以提前获取它们的列索引,用索引访问代替列名访问,进一步减少字符串查找开销。
  2. 关闭目标表的事件通知:在批量添加行前,调用dtImportParsed.BeginLoadData(),完成后调用dtImportParsed.EndLoadData(),避免每次添加行时触发不必要的事件。
  3. 减少字符串Trim操作:如果源数据本身没有多余空格,可以直接去掉.Trim(),减少字符串处理开销。

内容的提问来源于stack exchange,提问作者Jason Mills

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 10:46:19