279

IN句の値のリストがビジネスロジックからのものである場合に、Dapper ORMを使用してIN句を使用してクエリを作成する最良の方法は何ですか?たとえば、クエリがあるとします。

SELECT * 
  FROM SomeTable 
 WHERE id IN (commaSeparatedListOfIDs)

commaSeparatedListOfIDsビジネスロジックから渡されており、どのタイプでもかまいませんIEnumerable(of Integer)。この場合、どのようにクエリを作成しますか?これまで行ってきたこと、基本的には文字列の連結を行う必要がありますか、それとも私が気付いていない高度なパラメーターマッピング手法がありますか?

4

10 に答える 10

449

Dapper はこれを直接サポートします。例えば...

string sql = "SELECT * FROM SomeTable WHERE id IN @ids"
var results = conn.Query(sql, new { ids = new[] { 1, 2, 3, 4, 5 }});

Postgres を使用していない場合は、この回答を参照してください。

于 2011-12-05T16:20:05.083 に答える
73

GitHub プロジェクトのホームページから直接:

Dapper を使用すると、IEnumerable を渡すことができ、クエリが自動的にパラメーター化されます。

connection.Query<int>(
    @"select * 
      from (select 1 as Id union all select 2 union all select 3) as X 
      where Id in @Ids", 
    new { Ids = new int[] { 1, 2, 3 });

次のように翻訳されます。

select * 
from (select 1 as Id union all select 2 union all select 3) as X 
where Id in (@Ids1, @Ids2, @Ids3)

// @Ids1 = 1 , @Ids2 = 2 , @Ids2 = 3
于 2011-12-05T17:04:25.610 に答える
53

句が大きすぎて MSSQL で処理できない場合INは、Dapper で TableValueParameter を簡単に使用できます。

  1. MSSQL で TVP タイプを作成します。

    CREATE TYPE [dbo].[MyTVP] AS TABLE([ProviderId] [int] NOT NULL)
    
  2. DataTableTVP と同じ列で を作成し、値を入力します。

    var tvpTable = new DataTable();
    tvpTable.Columns.Add(new DataColumn("ProviderId", typeof(int)));
    // fill the data table however you wish
    
  3. INNER JOINTVP テーブルで実行するように Dapper クエリを変更します。

    var query = @"SELECT * FROM Providers P
        INNER JOIN @tvp t ON p.ProviderId = t.ProviderId";
    
  4. Dapper クエリ呼び出しで DataTable を渡します

    sqlConn.Query(query, new {tvp = tvpTable.AsTableValuedParameter("dbo.MyTVP")});
    

これは、複数の列の一括更新を行う場合にも素晴らしく機能します。単に TVP を作成し、TVPUPDATEへの内部結合を使用して実行します。

于 2017-03-30T21:22:11.523 に答える
17

また、次のようにクエリ文字列を括弧で囲まないようにしてください。

SELECT Name from [USER] WHERE [UserId] in (@ids)

これにより、Dapper 1.50.2 を使用して SQL 構文エラーが発生し、括弧を削除することで修正されました

SELECT Name from [USER] WHERE [UserId] in @ids
于 2018-03-05T20:01:24.647 に答える
16

これはおそらく、ID のリストを使用して Dapper で多数の行をクエリする最速の方法です。これは、あなたが考えることができる他のほとんどの方法よりも高速であることを約束します(別の回答で与えられたようにTVPを使用することを除いて、私はテストしていませんが、まだデータを入力する必要があるため、遅くなる可能性がありますTVP)。これは 、構文を使用する Dapper よりもはるか高速であり、行ごとに Entity Framework よりも高速ですまた、 orのリストを渡すよりもはるかに高速です。複数列キーを使用するように簡単に拡張できます。余分な列を、一時テーブル、および結合条件に追加するだけです。INVALUESUNION ALL SELECTDataTable

public IReadOnlyCollection<Item> GetItemsByItemIds(IEnumerable<int> items) {
   var itemList = new HashSet(items);
   if (itemList.Count == 0) { return Enumerable.Empty<Item>().ToList().AsReadOnly(); }

   var itemDataTable = new DataTable();
   itemDataTable.Columns.Add("ItemId", typeof(int));
   itemList.ForEach(itemid => itemDataTable.Rows.Add(itemid));

   using (SqlConnection conn = GetConnection()) // however you get a connection
   using (var transaction = conn.BeginTransaction()) {
      conn.Execute(
         "CREATE TABLE #Items (ItemId int NOT NULL PRIMARY KEY CLUSTERED);",
         transaction: transaction
      );

      new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, transaction) {
         DestinationTableName = "#Items",
         BulkCopyTimeout = 3600 // ridiculously large
      }
         .WriteToServer(itemDataTable);
      var result = conn
         .Query<Item>(@"
            SELECT i.ItemId, i.ItemName
            FROM #Items x INNER JOIN dbo.Items i ON x.ItemId = i.ItemId
            DROP TABLE #Items;",
            transaction: transaction,
            commandTimeout: 3600
         )
         .ToList()
         .AsReadOnly();
      transaction.Rollback(); // Or commit if you like
      return result;
   }
}

一括挿入について少し学ぶ必要があることに注意してください。トリガーの起動 (デフォルトは no)、制約の尊重、テーブルのロック、同時挿入の許可などに関するオプションがあります。

于 2017-01-25T23:54:04.410 に答える
3

私の経験では、これを処理する最も友好的な方法は、文字列を値のテーブルに変換する関数を用意することです。

Web には多くのスプリッター関数が用意されています。SQL のフレーバーが何であれ、簡単に見つけることができます。

その後、次のことができます...

SELECT * FROM table WHERE id IN (SELECT id FROM split(@list_of_ids))

または

SELECT * FROM table INNER JOIN (SELECT id FROM split(@list_of_ids)) AS list ON list.id = table.id

(または類似)

于 2011-12-05T16:05:31.253 に答える