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

如果您的IN子句对于 MSSQL 来说太大而无法处理,您可以很容易地将 TableValueParameter 与 Dapper 一起使用。

  1. 在 MSSQL 中创建 TVP 类型:

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

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

    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 回答
25

postgres 的示例:

string sql = "SELECT * FROM SomeTable WHERE id = ANY(@ids)"
var results = conn.Query(sql, new { ids = new[] { 1, 2, 3, 4, 5 }});
于 2019-10-18T09:51:09.353 回答
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

这可能是使用 Dapper 使用 ID 列表查询大量行的最快方法。我向你保证,这比你能想到的几乎任何其他方式都快(除了使用另一个答案中给出的 TVP 并且我没有测试过,但我怀疑可能会慢一些,因为你仍然需要填充电视节目)。它 使用语法比 Dapper 快行星,逐行比 Entity Framework 快。它甚至比传递一个或多个项目的列表还要快。它可以很容易地扩展为使用多列键,只需将额外的列添加到临时表和连接条件中。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 回答
11

不需要()像我们在常规 SQL 中那样添加 WHERE子句。因为 Dapper 会自动为我们做到这一点。这是syntax:-

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

var conditions = new { listOfIntegers };
    
var results = connection.Query(SQL, conditions);
于 2018-04-10T08:20:14.263 回答
4

就我而言,我使用了这个:

var query = "select * from table where Id IN @Ids";
var result = conn.Query<MyEntity>(query, new { Ids = ids });

我在第二行中的变量“ids”是一个 IEnumerable 字符串,我猜它们也可以是整数。

于 2015-07-15T12:49:06.967 回答
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 回答
0
SELECT * FROM tbl WHERE col IN @val

我还注意到这种语法不适用于byte[]. Dapper 只接受最后一个元素,并且参数必须包含在双亲中。但是,当我将类型更改为int[]一切正常时。

于 2021-12-07T09:13:50.213 回答