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

问题描述 投票:0回答:11

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

SELECT * 
  FROM SomeTable 
 WHERE id IN (commaSeparatedListOfIDs)

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

.net sql dapper
11个回答
520
投票

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

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

除非您使用 Postgres,在这种情况下请参阅 这个答案


82
投票

直接来自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

57
投票

如果您的

IN
子句对于 MSSQL 来说太大而无法处理,您可以轻松地将 TableValueParameter 与 Dapper 结合使用。

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

    CREATE TYPE [dbo].[MyTVP] AS TABLE([ProviderId] [int] NOT NULL)
    
  2. 创建一个与 TVP 具有相同列的

    DataTable
    并用值填充它

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

    INNER JOIN

    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 并使用 TVP 的内部联接执行

UPDATE


53
投票

postgres 示例:

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

25
投票

还要确保您的查询字符串没有用括号括起来,如下所示:

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

我在使用 Dapper 1.50.2 时遇到了这个问题,导致 SQL 语法错误,通过删除括号修复了

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

20
投票

最快的方法,没有之一

这是使用 ID 列表使用 Dapper 查询大量行的最快方法。

我向你保证,这比任何其他方式都要快,甚至比 TVP 还要快。

它比使用 IN 语法的 Dapper 快

planets
,并且比逐行实体框架快 universes。它甚至比传递
VALUES
UNION 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;
   }
}

批量插入

请注意,您需要了解一些有关批量插入的知识。有一些关于触发触发器(默认为否)、遵守约束、锁定表、允许并发插入等的选项。例如,批量插入仅在事务日志中创建单个条目。这就是他们如此之快的原因之一。


15
投票

没有必要像在常规 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);

4
投票

就我而言,我用过这个:

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

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


1
投票

根据我的经验,处理此问题最友好的方法是使用一个将字符串转换为值表的函数。

网络上有许多可用的拆分器函数,如果您喜欢 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

(或类似)


0
投票
SELECT * FROM tbl WHERE col IN @val

我还注意到这种语法不适用于

byte[]
。 Dapper 仅采用最后一个元素,并且参数必须用括号括起来。 但是,当我将类型更改为
int[]
时,一切正常。


-2
投票

对于 PostgreSQL,我发现字符串插值在 .NET 中对我很有用。

示例:

var ids = new int[] { 1, 2, 3 };
var query = "SELECT name FROM table WHERE id IN ({string.Join(",", ids)})";
using var connection = _dapperContext.CreateConnection();
var results = await connection.QueryAsync<ResultModel>(query);
© www.soinside.com 2019 - 2024. All rights reserved.