1

編集:
私の問題はもう問題ではありません: パフォーマンス テストをやり直しましたが、致命的な愚かなエラーが発生しました: ミリ秒から秒を取得するために x1000 を忘れていました :/ 申し訳ありません。
情報:
- PC からローカル ネットワーク上のデータベース サーバーに 1 秒あたり約 1900 の更新を行っています。
- プログラムが DB と同じマシン上にある場合、毎秒 3.200 の更新。
- データベース サーバー上の PC から 1 秒あたり 3.500 の更新 新しい SQLConnection を再作成して再度開くことはありません。
- バッチ テキストで 1 秒あたり 5.800 の更新。私の 10.000 行の場合、5 秒かかる場合、私のプログラムでは問題ありません。ご心配をおかけして申し訳ありません。

実際には、SQL ストアド プロシージャを使用してデータベースに行を作成し、SQL インジェクションを回避しています。C# では、次のメソッドがあります。

public void InsertUser(string userCode)
{
   using (SqlConnection sqlConnection = new SqlConnection(this.connectionString))
   {
      SqlCommand sqlCommand = new SqlCommand("InsertUser", sqlConnection);
      sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
      sqlCommand.Parameters.Add(new SqlParameter("@UserCode", userCode));
      sqlConnection.Open();
      sqlCommand.ExecuteNonQuery();///0.2 seconds !ERROR HERE! 0.2ms here,NOT 0.2sec!!!
   }
} 

挿入する行が 1 つまたは 2 つある場合に最適です。しかし、1,000 人のユーザーと 10,000 の製品と 5,000 のペットを作成する必要がある場合、それは最善の解決策ではありません。ネットワーク トランスポートで膨大な時間を失うことになります。

チェックインしなくても、限られた量のコールバックしか使用できないと思います。だから私は10.000回呼び出したくありません:

sqlCommand.BeginExecuteNonQuery()

もう 1 つの方法は、バッチ テキストを作成することですが、SQL インジェクションのリスクがあります (それは醜いです)。

.Net でそれを管理する「SqlCommandList」オブジェクトはありますか? データベースに大量の書き込みを行うにはどうすればよいですか? そのための良いパターンは何ですか?

4

9 に答える 9

10

これは少し速く実行されるはずです:

public void InsertUser(IEnumerable<string> userCodes)
{
   using (SqlConnection sqlConnection = new SqlConnection(this.connectionString), 
             SqlCommand sqlCommand = new SqlCommand("InsertUser", sqlConnection))
   {
      sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
      SqlParameter param = sqlCommand.Parameters.Add("@UserCode", SqlDbTypes.VarChar);
      sqlConnection.Open();

      foreach(string code in userCodes)
      {
          param.Value = code;
          sqlCommand.ExecuteNonQuery();///0.2 seconds
      }
   }
}

これにより、1000 ユーザーを渡しても、1 つの接続しか開かれず、1 つのコマンドしか作成されません。ただし、各挿入は個別に行われます。もちろん、userCode が文字列でない場合は、適切にリファクタリングする必要があります。また、SQL Server のBULK INSERTコマンドを調べることもできます。

于 2008-10-14T12:56:30.800 に答える
4

SQLDataAdaptor の UpdateBatchSize はどうですか?

フロントエンド担当者はこれを使用して、数 10,000 の proc 呼び出しをチャンクにバッチ処理します

論文

MSDN

私たちの環境では「bulkadmin」権限が許可されていないため、BULKINSERT/bcp などを使用できません。

于 2008-10-14T18:44:11.817 に答える
2

個人的には、かなり大きな挿入を定期的に行うことを期待している場合 (10,000 行が確実に該当します...)、受信データ用に別のテーブルを用意し、SqlBulkCopyこのテーブルを使用することを検討するかもしれません。次に、データを実際のテーブルに移動する単一のストアド プロシージャを実行するだけです。

別のアプローチは、xml をデータベースに送信し、sqlxml を使用してそれを解析することです (SQL2005 以降でははるかに簡単です)。しかし、これは db サーバーに余分な作業を課します。

于 2008-10-14T12:46:24.457 に答える
2

これについて本当に心配している場合は、(あなたが言ったように)次のようにコマンドを文字列にまとめることができます。

var cmd = new SqlCommand();
cmd.Connection = sqlConnection;
for (int i = 0; i < batchSize; i++) {
    cmd.CommandText += String.Format("EXEC InsertUser @UserCode{0};", i);
    cmd.Parameters.AddWithValue("@UserCode" + i.ToString(), XXXXX);
    //... etc ...
}

このスキームではパラメーターを使用するため、ストアド プロシージャを使用した場合よりも SQL インジェクションのリスクが高くなりません。しかし、これを行うことで本当にかなりの時間を節約できるかどうかは疑問です。IMO はシンプルに保ち、現在行っている方法で行う必要があります。

于 2008-10-14T12:58:35.460 に答える
2

Joel の回答に基づいて、これは SqlBulkCopy を使用するか、厄介な SQL の大きな文字列を作成して実行する以外の最速のソリューションです。(パフォーマンスを大幅に向上させるトランザクションを追加しました)

public void InsertUser(IEnumerabler<string> userCodes)
{
   using (SqlConnection sqlConnection = new SqlConnection(this.connectionString))
   {
      sqlConnection.Open();
      SqlTransaction transaction = connection.BeginTransaction();
      SqlCommand sqlCommand = new SqlCommand("InsertUser", sqlConnection);
      sqlCommand.Transaction = transaction;
      sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
      SqlParameter param = sqlCommand.Parameters.Add("@UserCode", SqlDbTypes.VarChar);

      foreach(string code in userCodes)
      {
          param.Value = code;
          sqlCommand.ExecuteNonQuery();
      }      
      transaction.Commit();
   }
}
于 2008-10-14T13:13:35.353 に答える
1

これはかなり古い質問だと思います。

SQL Server 2008 では、テーブル値パラメーターを使用するのが答えです。つまり、すべての変数を使用済みの定義済みタイプ (テーブル) に渡します。

SQL では、すべてのレコードを個別のアイテムとして処理できるようになりました...実際にセット ロジックを使用して、実際のパフォーマンスを得ることができます。

于 2011-12-12T18:11:27.863 に答える
0

XML ドキュメントをストアド プロシージャに渡し、それを繰り返し処理して挿入するデータを見つけることを検討したことがありますか?

于 2008-10-14T12:45:38.747 に答える
0

「それは最善の解決策ではありません。ネットワーク トランスポートで膨大な時間を失うことになります」

これがあなたが頻繁にしないことなら、それは問題ですか?最初に測定し、問題がある場合は修正します。個人的には、受信挿入用に Marc Gravells テーブルを使用するでしょう。もう 1 つのオプションは、挿入を非同期で開始することです。そうすれば、それぞれが終了するのを待たずに次の挿入を開始できます。

何年もかかりましたが、最終的に、コードを必要としない最適化に時間を費やすべきではないことに気付きました。

これが役に立てば幸いです(そうは思いませんが、申し訳ありません)。

于 2008-10-14T12:56:20.053 に答える
0

上記の回答のいくつかによると、最小限の労力で最も顕著なパフォーマンスの向上には、既存のコードへの 2 つの変更が含まれます。

  1. 更新をトランザクションにラップする
  2. 1 つの接続のみを開き、異なるパラメーターを使用してプロシージャを複数回呼び出す。

BULK INSERT はオプションですが、やりたいことにはおそらくやり過ぎです。

于 2008-10-14T13:32:54.783 に答える