1

使用 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;                
    }    
4

7 回答 7

2
  1. 您是否使用 StringBuilder 来连接字符串,而不仅仅是常规的字符串连接?
  2. 您是否真的需要从数据库中拉回更多列?如果是这样,尽量不要。只拉回您需要的列。
  3. 您是否真的需要从数据库中拉回更多行?如果是这样,尽量不要。只拉回您需要的行。
  4. 电脑有多少内存?当您运行程序或接近它时,它是最大化吗?处理器是最大还是完全?如果您使用的内存过多,则可能需要进行更多的流式传输。这意味着不要将整个结果集拉入内存(即数据表),而是一次读取每一行。这也可能意味着您可能需要将结果附加到文件中,以免占用太多内存,而不是将结果连接到字符串(或 StringBuilder )中。
于 2012-05-24T16:44:30.523 回答
0

我把工作分成小块,让每一块由它自己的线程处理。您可以通过改变 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;
}
于 2012-05-24T18:44:24.037 回答
0

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

 string CSVValues = String.Join(",", dtOutput.AsEnumerable()
                                                .Where(a => a[0].ToString() == value)
                                                .Select(b => b[2].ToString()));
于 2012-05-24T16:40:32.250 回答
0

第 1 步 - 通过分析器运行它,确保在优化时查看正确的内容。

举个例子,我们有一个问题,我们确信数据库交互很慢,当我们运行分析器时,数据库几乎没有出现。

也就是说,可能要尝试的事情:

  • 如果您有可用内存,请将查询转换为列表,这将强制读取完整的数据库。否则,linq 可能会分块加载执行多个数据库查询。
  • 将工作推送到数据库 - 如果您可以创建一个查询而不是修剪您正在查看的数据,甚至为您计算字符串,那可能会更快
  • 如果这是经常运行查询但数据很少更改的情况,如果您使用的是远程数据库,请考虑将数据复制到本地数据库(例如 sqlite)。
  • 如果您使用的是本地 sql-server,请尝试使用 sqlite,它在很多方面都更快。
于 2012-05-24T16:45:11.350 回答
0
var value = dataTable
            .AsEnumerable()
            .Select(row => row.Field<string>("columnName"));

var colValueStr = string.join(",", value.ToArray());
于 2012-05-24T16:45:29.757 回答
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);

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

于 2012-05-24T16:46:43.183 回答
0

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

Parallel.ForEach(dataTable.AsEnumerable(),
            item => { str += ((item as DataRow)["ColumnName"]).ToString(); });
于 2012-05-24T17:57:38.913 回答