2016-12-03 7 views
1

私はC#とSQL Serverを使用しています。私は各ドキュメントの行を持っているSQL Serverのテーブルの主キーに対応するドキュメントのIDのリストを持っていて、その行にはそのIDのIDとドキュメントが含まれています。各IDの行の文書を取得したいと思います。現在、IDごとにクエリを実行していますが、10,000個あるため、大量のクエリが実行され、非常に時間がかかります。それは、テーブルからメモリにすべてをロードしてから、私が持っているIDでフィルタリングするほうが速くなってしまいますが、効率が悪く、時間が経ってもスケールされません。それが意味をなさない場合は、うまくいけば、実行に時間がかかる次のコードが私がしようとしていることを示していることを願っています。IDリストから多くの行を取得

private static Dictionary<Guid, string> foo(IEnumerable<Guid> guids, SqlConnection conn) 
    { 
     using (SqlCommand command = new SqlCommand(null, conn)) 
     { 
      command.CommandText = "select document from Documents where id = @id"; 
      SqlParameter idParam = new SqlParameter("@id", SqlDbType.UniqueIdentifier); 
      command.Parameters.Add(idParam); 
      command.Prepare(); 

      var documents = new Dictionary<Guid, string>(); 
      foreach (var guid in guids) 
      { 
       idParam.Value = guid; 
       object obj = command.ExecuteScalar(); 
       if (obj != null) 
       { 
        documents[guid] = (string)obj; 
       } 
      } 
      return documents; 
     } 
    } 

私はプログラム的にこのようなwhere句を使用するクエリ文字列を構築することができます:一度に100件のドキュメントを取得するには "(ID1、ID2、ID3、...、ID100)のidがどこ...." かそのようなものですが、これはジャッキーを感じ、より良い方法になるはずです。

私はこれに遭遇する唯一の人ではないと確信しています。これについては受け入れられる方法がありますか?

+0

わかりませんが、送信できるクエリのサイズに制限はありませんか? idはguidで、それぞれ36バイトになります。したがって、もし私が20,000のIDを持っていれば、72,000バイト以上のクエリです。そして、これは何十万人もの人に成長するかもしれません。クエリのサイズに制限はありませんか? – cabird

答えて

4

あなたは、あなたがSQL Serverの

にパラメータの種類を作成する必要があります

まずに必要なすべてのIDがでSqlParameterを作成するコードでのGUID

の量の制限なしでTable-Valued Parametersを使用することができます

CREATE TYPE IdTableType AS TABLE 
( 
    Id uniqueidentifier 
); 

そしてコードで

private static Dictionary<Guid, string> foo(IEnumerable<Guid> guids, SqlConnection conn) 
{ 
    using (SqlCommand command = new SqlCommand(null, conn)) 
    { 
     // use parameter as normal table in the query 
     command.CommandText = 
      "select document from Documents d inner join @AllIds a ON d.id = a.Id"; 

     // DataTable is used for Table-Valued parameter as value 
     DataTable allIds = new DataTable(); 
     allIds.Columns.Add("Id"); // Name of column need to be same as in created Type 

     foreach(var id in guids) 
      allids.Rows.Add(id); 

     SqlParameter idParam = new SqlParameter 
     { 
      ParameterName = "@AllIds", 
      SqlDbType=SqlDbType.Structured // Important for table-valued parameters 
      TypeName = "IdTableType", // Important! Name of the type must be provided 
      Value = allIds 
     }; 
     command.Parameters.Add(idParam); 

     var documents = new Dictionary<Guid, string>(); 
     using (var reader = command.ExecuteReader()) 
     { 
      while (reader.Read()) 
      { 
       documents[guid] = reader[0].ToString(); 
      }    
     } 

     return documents; 
    } 
} 

これ以上コマンドを準備する必要はありません。最初の実行の後に、クエリテキストが同じままであるので、次のクエリは同じコンパイルされたクエリプランを使用します。

+1

このメソッドは素晴らしい動作します。大量のIDでもうまく実行されます(IN句とは異なります) – jleach

+0

これは素晴らしいです。私はそれを実行しようとしているが、私はそれが明示的な型を必要とする例外が発生します。 idParam.SqlDbType = SqlDbType.Structuredを設定すると問題が修正されたようです。それは正しいと思いますか?これは、可変長パラメータにサイズが必要であるという例外があります。私はこれをGuidのサイズかGuidのアイテム数の大きさに設定するのかどうかはわかりませんが、それをかなり大きな数に設定するとうまくいくようです。最後に、 "all"はキーワードなので、クエリの有効な識別子に変更して、うまくいきます。これは素晴らしい解決策です。ありがとう。 – cabird

+0

@cabird、あなたは 'SqlDbType = SqlDbType.Structured'について提供する必要があります。長さについて:私は更新された答えで言及したように、 'command.Prepare'をもう使用する必要はありません – Fabio

0

これらをIDのセットに束ね、テーブル値のパラメータをクエリに渡すことができます。あなたがアップし、あなたの読み取りバッチにする必要がありますので、

connection.Query("select document from Documents where id in @ids", new { ids = guids}); 

SQLでをtheresしかし8000パラメータの制限に注意:Dapperのでは、これは次のように少し見えます。

btw ..このタイプのデータアクセスには、Dapperまたは別のマイクロオーマーを検討することを強くお勧めします。

関連する問題