7

私は既存のアプリケーションに取り組んでいます。このアプリケーションは、巨大なファイルからデータを読み取り、いくつかの計算を行った後、そのデータを別のテーブルに格納します。

しかし、これを行うループ (以下を参照) には非常に長い時間がかかります。ファイルには数千のレコードが含まれることがあるため、プロセス全体に数日かかります。

foreachこのループを別のものに置き換えることはできますか? 使っParallel.ForEachてみましたが助かりました。私はこれに慣れていないので、あなたの助けに感謝します。

foreach (record someredord Somereport.r)
{
    try
    {
        using (var command = new SqlCommand("[procname]", sqlConn))
        {
            command.CommandTimeout = 0;
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.Add(…);

            IAsyncResult result = command.BeginExecuteReader();
            while (!result.IsCompleted)
            {
                System.Threading.Thread.Sleep(10);
            }
            command.EndExecuteReader(result);
        }
    }
    catch (Exception e)
    {
        …
    }
}

回答を確認した後、非同期を削除し、以下のようにコードを編集して使用しました。しかし、これはパフォーマンスを向上させませんでした。

using (command = new SqlCommand("[sp]", sqlConn))
{
    command.CommandTimeout = 0;
    command.CommandType = CommandType.StoredProcedure;
    foreach (record someRecord in someReport.)
    {
        command.Parameters.Clear();
        command.Parameters.Add(....)
        command.Prepare();                            

        using (dr = command.ExecuteReader())
        {
            while (dr.Read())
            {
                if ()
                {

                }
                else if ()
                {

                }
            }
        }                             
    }                        
}
4

6 に答える 6

8

SQL接続を何度もループする代わりに、SQLサーバーからデータのセット全体を抽出し、データセットを介してデータを処理することを検討したことがありますか?

編集:私が何を意味するのかをさらに説明することにしました..次のような疑似コードを次のように実行できます

  1. select *を使用して、データベースからすべての情報を取得し、それらをクラスまたは辞書のリストに格納します。
  2. foreach(someRecordをsomeReportに記録)を実行し、通常どおり条件の一致を実行します。
于 2012-08-30T17:20:58.843 に答える
6

ステップ 1:非同期での試行をやめます。適切に実装されておらず、とにかくブロックしています。したがって、手順を実行して、それが役立つかどうかを確認してください。

ステップ 2: SqlCommand をループの外に移動し、反復ごとに再利用します。そうすれば、ループ内のすべてのアイテムに対して作成および破棄するコストが発生しません。

警告:前回の反復から不要なパラメーターをリセット/クリア/削除してください。オプションのパラメーターを使用してこのようなことを行い、必要のないパラメーターをクリーンアップしなかったため、前の反復から「ブリードスルー」がありました!

于 2012-08-30T17:23:30.043 に答える
3

あなたの最大の問題はあなたがこれをループしているということです:

IAsyncResult result = command.BeginExecuteReader();

while (!result.IsCompleted)
{
   System.Threading.Thread.Sleep(10);
}

command.EndExecuteReader(result);

非同期モデルの全体的な考え方は、呼び出しスレッド(このループを実行するスレッド)は、Endメソッドで結果の処理を開始する前に、Beginメソッドを使用してすべての非同期タスクをスピンアップする必要があるということです。メインの呼び出しスレッド内でThread.Sleep()を使用して、非同期操作が完了するのを待機している場合(ここにいるように)、それは間違っています。最終的には、各コマンドが一度に1つずつ発生します。 、が呼び出されてから、次のが開始するまで待機しています。

代わりに、次のようなものを試してください。

public void BeginExecutingCommands(Report someReport)
{
    foreach (record someRecord in someReport.r) 
    {
        var command = new SqlCommand("[procname]", sqlConn);

        command.CommandTimeout = 0;
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(…);

        command.BeginExecuteReader(ReaderExecuted, 
            new object[] { command, someReport, someRecord });                   
    }
}

void ReaderExecuted(IAsyncResult result)
{
    var state = (object[])result.AsyncState;
    var command = state[0] as SqlCommand;
    var someReport = state[1] as Report;
    var someRecord = state[2] as Record;

    try
    {
        using (SqlDataReader reader = command.EndExecuteReader(result))
        {
            // work with reader, command, someReport and someRecord to do what you need.
        }
    }
    catch (Exception ex)
    {
        // handle exceptions that occurred during the async operation here
    }
}
于 2012-08-30T17:39:08.380 に答える
1

コメントで話していたように、このデータをメモリに保存して操作する方が、より効率的なアプローチになる可能性があります。

したがって、これを行う簡単な方法の1つは、EntityFrameworkから始めることです。Entity Frameworkは、データベーススキーマに基づいてクラスを自動的に生成します。次に、SELECTステートメントを保持するストアドプロシージャをインポートできます。ストアドプロシージャをEFにインポートすることをお勧めする理由は、このアプローチは、EFに対してLINQでクエリを実行するよりも一般的に効率的だからです。

次に、ストアドプロシージャを実行し、次のListようにデータを格納します...

var data = db.MyStoredProc().ToList();

その後、あなたはそれであなたがやりたいことを何でもすることができますdata。または、前述したように、主キーで多くのルックアップを実行している場合は、次のToDictionary()ようなものを使用してください...

var data = db.MyStoredProc().ToDictionary(k => k.MyPrimaryKey);

dataいずれにせよ、この時点でメモリ内で作業することになります。

于 2012-08-30T21:40:42.790 に答える
1

SQL では、書き込みのもう一方の端は (1 つの) ディスクです。並列で高速に書き込むことはめったにありません。実際、並行して実行すると、インデックスの断片化により速度が低下することがよくあります。ロードする前に、プライマリ (クラスター化された) キーでデータを並べ替えることができる場合。大きな負荷では、他のキーを無効にしても、データを再構築するキーをロードします。

非同期で何をしているのかはよくわかりませんが、それ自体が待機していたため、期待どおりのことをしていなかったことは確かです。

try
{
    using (var command = new SqlCommand("[procname]", sqlConn))
    {
        command.CommandTimeout = 0;
        command.CommandType = CommandType.StoredProcedure;

        foreach (record someredord Somereport.r)
        {
            command.Parameters.Clear()
            command.Parameters.Add(…);

            using (var rdr = command.ExecuteReader())
            {
                while (rdr.Read())
                {
                    …
                }
            }
        }
    }
}
catch (…)
{
    …
}
于 2012-08-30T17:41:42.420 に答える
0

コマンドを実行するSQLと、必要なリソースがロックされるようです。そのため、Asyncメソッドを使用する必要があります(私の推測です)。

データベースが使用されていない場合は、データベースへの排他的アクセスを試してください。それでも、データモデルの複雑さのためにいくつかの内部トランザクションがあり、データベース設計者に相談することを検討してください。

于 2012-08-30T17:40:04.470 に答える