C#:通过一个巨大的数据表迭代

时间:2012-05-24 16:35:05

标签: c# loops datatable

使用for循环迭代包含大约40 000条记录的数据表需要将近4分钟。在循环内部,我只是读取每行特定列的值并将其合并为一个字符串。

我没有打开任何数据库连接或其他东西,因为它是一个接收数据表的函数,迭代它并返回一个字符串。

有没有更快的方法呢?

代码在这里:

   private string getListOfFileNames(Datatable listWithFileNames)
   {     
        string whereClause = "";

            if (listWithFileNames.Columns.Contains("Filename"))
            {
                whereClause = "where filename in (";
                for (int j = 0; j < listWithFileNames.Rows.Count; j++)
                    whereClause += " '" + listWithFileNames.Rows[j]["Filename"].ToString() + "',";
            }
            whereClause = whereClause.Remove(whereClause.Length - 1, 1);
            whereClause += ")";    

        return whereClause;                
    }    

7 个答案:

答案 0 :(得分:2)

  1. 您是否使用StringBuilder来连接字符串而不仅仅是常规的字符串连接?
  2. 您是否需要从数据库中删除更多列?如果是这样,尽量不要。只需拉回所需的色谱柱。
  3. 你是否从数据库中撤回了更多行,那么你真的需要吗?如果是这样,尽量不要。只需拉回你需要的行。
  4. 这台电脑有多少内存?当你运行程序或接近它时它是最大的?处理器最多还是处于最大程度?如果你使用太多内存,那么你可能需要做更多的流媒体。这意味着不将整个结果集拉入内存(即数据表),而是一次读取每一行。它也可能意味着不是将结果连接到字符串(或StringBuilder),而是可能需要将它们附加到文件中,以免占用太多内存。

答案 1 :(得分:0)

以下linq语句在第一列上有一个where子句,并在变量中连接第三列。

 string CSVValues = String.Join(",", dtOutput.AsEnumerable()
                                                .Where(a => a[0].ToString() == value)
                                                .Select(b => b[2].ToString()));

答案 2 :(得分:0)

第1步 - 通过分析器运行它,确保在优化时看到正确的东西。

例如,我们遇到了一个问题,我们确信这是一个缓慢的数据库交互,当我们运行分析器时,db几乎没有出现。

那说,可以尝试的事情:

  • 如果您有可用的内存,请将查询转换为列表,这个 将强制完整的数据库读取。否则linq可能会加载 块进行多个数据库查询。
  • 将工作推送到数据库 - 如果您可以创建查询而不是减少 您正在查看的数据,甚至为您计算字符串, 可能会更快
  • 如果这是经常运行查询但很少数据的地方 如果更改,请考虑将数据复制到本地数据库(例如sqlite) 你正在使用远程数据库。
  • 如果您正在使用本地sql-server,请尝试sqlite,它的速度更快 很多事情。

答案 3 :(得分:0)

var value = dataTable
            .AsEnumerable()
            .Select(row => row.Field<string>("columnName"));

var colValueStr = string.join(",", value.ToArray());

答案 4 :(得分:0)

尝试在表格中添加带表达式的虚拟列。像这样:

DataColumn dynColumn = new DataColumn();

{
    dynColumn.ColumnName = "FullName";
    dynColumn.DataType = System.Type.GetType("System.String");
    dynColumn.Expression = "LastName+' '-ABC";
}
UserDataSet.Tables(0).Columns.Add(dynColumn);

稍后在您的代码中,您可以使用此虚拟列。您不需要旋转任何循环来连接字符串。

答案 5 :(得分:0)

尝试使用parallel for循环.. 这是示例代码..

Parallel.ForEach(dataTable.AsEnumerable(),
            item => { str += ((item as DataRow)["ColumnName"]).ToString(); });

答案 6 :(得分:0)

我已将作业分成小块,并让每个作品都由自己的Thread处理。您可以通过改变nthreads数来微调线程数。尝试使用不同的数字,以便您可以看到性能上的差异。

private string getListOfFileNames(DataTable listWithFileNames)
{
    string whereClause = String.Empty;

    if (listWithFileNames.Columns.Contains("Filename"))
    {
        int nthreads = 8; // You can play with this parameter to fine tune and get your best time.
        int load = listWithFileNames.Rows.Count / nthreads; // This will tell how many items reach thread mush process.

        List<ManualResetEvent> mres = new List<ManualResetEvent>(); // This guys will help the method to know when the work is done.
        List<StringBuilder> sbuilders = new List<StringBuilder>(); // This will be used to concatenate each bis string.

        for (int i = 0; i < nthreads; i++)
        {
            sbuilders.Add(new StringBuilder()); // Create a new string builder
            mres.Add(new ManualResetEvent(false)); // Create a not singaled ManualResetEvent.

            if (i == 0) // We know were to put the very begining of your where clause
            {
                sbuilders[0].Append("where filename in (");
            }

            // Calculate the last item to be processed by the current thread
            int end = i == (nthreads - 1) ? listWithFileNames.Rows.Count : i * load + load;

            // Create a new thread to deal with a part of the big table.
            Thread t = new Thread(new ParameterizedThreadStart((x) =>
            {
                // This is the inside of the thread, we must unbox the parameters
                object[] vars = x as object[];
                int lIndex = (int)vars[0];
                int uIndex = (int)vars[1];
                ManualResetEvent ev = vars[2] as ManualResetEvent;
                StringBuilder sb = vars[3] as StringBuilder;
                bool coma = false;

                // Concatenate the rows in the string builder
                for (int j = lIndex; j < uIndex; j++)
                {
                    if (coma)
                    {
                        sb.Append(", ");
                    }
                    else
                    {
                        coma = true;
                    }

                    sb.Append("'").Append(listWithFileNames.Rows[j]["Filename"]).Append("'");
                }

                // Tell the parent Thread that your job is done.
                ev.Set();
            }));

            // Start the thread with the calculated params
            t.Start(new object[] { i * load, end, mres[i], sbuilders[i] });
        }

        // Wait for all child threads to finish their job
        WaitHandle.WaitAll(mres.ToArray());

        // Concatenate the big string.
        for (int i = 1; i < nthreads; i++)
        {
            sbuilders[0].Append(", ").Append(sbuilders[i]);
        }

        sbuilders[0].Append(")"); // Close your where clause

        // Return the finished where clause
        return sbuilders[0].ToString();
    }

    // Returns empty
    return whereClause;
}