SELECT*FROM X WHERE使用Dapper ORM标识IN(...)

Mar*_*rko 209 .net sql dapper

当IN子句的值列表来自业务逻辑时,使用Dapper ORM使用IN子句编写查询的最佳方法是什么?例如,假设我有一个查询:

SELECT * 
  FROM SomeTable 
 WHERE id IN (commaSeparatedListOfIDs)
Run Code Online (Sandbox Code Playgroud)

commaSeparatedListOfIDs是从业务逻辑传入的,它可以是任何类型的IEnumerable(of Integer).在这种情况下,我将如何构造查询?我是否必须做到目前为止我一直在做的事情,这基本上是字符串连接,还是有一些我不知道的高级参数映射技术?

Luk*_*keH 333

Dapper直接支持这一点.例如...

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

  • 我认为重要的是要注意,您可以在阵列中发送的项目数量有限.当我传入太多的ID时,我意识到这很难.我不记得确切的数字但是从我的记忆中我认为在Dapper停止工作/执行查询之前它是200个元素. (43认同)
  • 仅供参考 - SQL Server 2008 R2在`IN`子句中限制为2100个条目. (23认同)
  • 马可,这很重要.而且,如果您这样做,您可能会考虑寻找另一种查询数据的方法,例如进行连接或反连接,而不是传递ID列表.IN子句不是性能最高的查询,通常可以用exists子句替换,后者会更快. (7认同)
  • 注意:在SQL Server中,如果数组中有多个项目并将参数括在括号中,则会失败.删除括号将解决问题. (7认同)
  • 而SQLite的默认限制为999个变量. (6认同)
  • @jira我在这里回答了/sf/ask/587166541/#58448315 (3认同)
  • 在 sql server 2008 中,有没有人有解决方案可以用 TVP 替换它以克服限制? (2认同)
  • 对我不起作用。我正在使用 postgres。参数被替换为“$1”,它被传递到服务器,我收到语法错误。有什么想法如何让它发挥作用吗? (2认同)

Fac*_*tic 60

直接来自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 });
Run Code Online (Sandbox Code Playgroud)

将被翻译为:

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
Run Code Online (Sandbox Code Playgroud)


Mr.*_*. T 37

如果你的IN子句对于MSSQL来说太大了,你可以很容易地将TableValueParameter与Dapper一起使用.

  1. 在MSSQL中创建您的TVP类型:

    CREATE TYPE [dbo].[MyTVP] AS TABLE([ProviderId] [int] NOT NULL)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建一个DataTable与TVP相同的列,并用值填充它

    var tvpTable = new DataTable();
    tvpTable.Columns.Add(new DataColumn("ProviderId", typeof(int)));
    // fill the data table however you wish
    
    Run Code Online (Sandbox Code Playgroud)
  3. 修改您的Dapper查询以INNER JOIN在TVP表上执行:

    var query = @"SELECT * FROM Providers P
        INNER JOIN @tvp t ON p.ProviderId = t.ProviderId";
    
    Run Code Online (Sandbox Code Playgroud)
  4. 在Dapper查询调用中传递DataTable

    sqlConn.Query(query, new {tvp = tvpTable.AsTableValuedParameter("dbo.MyTVP")});
    
    Run Code Online (Sandbox Code Playgroud)

当您想要对多列进行大规模更新时,这也非常有用 - 只需构建一个TVP并UPDATE使用内部联接进行TVP.

  • 很好的解决方案,但不适用于 .Net Core,请参阅此问题:/sf/ask/2879264531/ -net-core-right-now。另请参阅此页面:https://github.com/StackExchange/Dapper/issues/603 (2认同)
  • 您可能还想考虑将`MyTVP`上的`ProviderId`设为'PRIMARY KEY CLUSTERED`,因为这只是解决了我们的性能问题(我们传递的值不包含重复项). (2认同)

San*_*́́́ 19

postgres 示例:

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

  • 谢谢你!我的 postgres 数据库不喜欢在查询中使用“IN”,但当我使用“ANY”时它工作得很好 (3认同)

Eri*_*ikE 12

这可能是使用ID列表使用Dapper查询大量行的最快方法.我向你保证,这比你能想到的几乎任何其他方式都要快(除了在另一个答案中使用TVP的可能例外,我没有测试过,但我怀疑可能会因为你仍然需要填充而变慢TVP).它是 行星比小巧玲珑的使用速度更快IN的语法和语义层比排实体框架排得更快.它甚至比传递列表VALUESUNION ALL SELECT项目更快.它可以很容易地扩展为使用多列键,只需将额外的列添加到DataTable临时表和连接条件.

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;
   }
}
Run Code Online (Sandbox Code Playgroud)

请注意,您需要了解有关批量插入的一些信息.有关触发触发器的选项(默认为no),尊重约束,锁定表,允许并发插入等等.

  • 当您可以使用表值参数来代替时,这是一个荒谬的工作量。Dapper 完全支持将 DataTable 作为 TVP 传递,这使您无需创建和销毁临时表以及通过 BulkCopy 填充该临时表。当 IN 子句的参数数量过多时,我们通常使用基于 TVP 的解决方案。 (2认同)
  • 这不是一件荒谬的工作,特别是如果使用助手类或扩展方法将其抽象一些。 (2认同)

Bri*_*den 9

还要确保不要像这样在查询字符串周围加上括号:

SELECT Name from [USER] WHERE [UserId] in (@ids)
Run Code Online (Sandbox Code Playgroud)

我使用Dapper 1.50.2导致了SQL语法错误,已通过删除括号进行了修复

SELECT Name from [USER] WHERE [UserId] in @ids
Run Code Online (Sandbox Code Playgroud)


Cod*_*ute 7

这是没有必要添加()在WHERE子句中,我们在一个普通的SQL做.因为Dapper会自动为我们做这件事.这是syntax: -

const string SQL = "SELECT IntegerColumn, StringColumn FROM SomeTable WHERE IntegerColumn IN @listOfIntegers";

var conditions = new { listOfIntegers };

var results = connection.Query(SQL, conditions);
Run Code Online (Sandbox Code Playgroud)