1

for ループを使用して約 40,000 レコードを含むデータテーブルを反復処理するには、ほぼ 4 分かかります。ループ内では、各行の特定の列の値を読み取り、それを文字列に連結しています。

データテーブルを受け取り、それを繰り返し処理して文字列を返す関数であるため、DB接続などを開いていません。

これを行うより速い方法はありますか?

コードは次のとおりです。

   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. コンピュータにはどのくらいのメモリがありますか?プログラムを実行したり、プログラムに近づいたりすると、最大になりますか?プロセッサは最大ですか、それともまったくですか?あまりにも多くのメモリを使用している場合は、より多くのストリーミングを行う必要があるかもしれません。これは、結果セット全体をメモリ(つまりデータテーブル)にプルするのではなく、各行を一度に1つずつ読み取ることを意味します。また、結果を文字列(または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

ステップ1-プロファイラーを介して実行し、最適化するときに正しいものを見ていることを確認します。

適切な例として、データベースの相互作用が遅いことが確実であり、プロファイラーを実行したときにデータベースがほとんど表示されないという問題がありました。

そうは言っても、試すことができること:

  • 使用可能なメモリがある場合は、クエリをリストに変換します。これにより、完全なデータベース読み取りが強制されます。そうしないと、linqは複数のdbクエリを実行するチャンクでロードされる可能性があります。
  • 作業をデータベースにプッシュします-見ているデータを切り詰めるよりもクエリを作成できる場合、または文字列を計算する場合でも、より高速になる可能性があります
  • これがクエリが頻繁に実行されるものであるが、データがめったに変更されない場合、リモートデータベースを使用している場合は、データをローカルデータベース(例: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

次の linq ステートメントには、最初の列に where 句があり、変数の 3 番目の列を連結します。

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

並列 for ループを使用してみてください。サンプル コードは次のとおりです。

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