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

SELECT * 
  FROM SomeTable 
 WHERE id IN (commaSeparatedListOfIDs)

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


当前回答

如果你的IN子句太大,MSSQL无法处理,你可以很容易地使用一个TableValueParameter与Dapper。

Create your TVP type in MSSQL: CREATE TYPE [dbo].[MyTVP] AS TABLE([ProviderId] [int] NOT NULL) Create a DataTable with the same column(s) as the TVP and populate it with values var tvpTable = new DataTable(); tvpTable.Columns.Add(new DataColumn("ProviderId", typeof(int))); // fill the data table however you wish Modify your Dapper query to do an INNER JOIN on the TVP table: var query = @"SELECT * FROM Providers P INNER JOIN @tvp t ON p.ProviderId = t.ProviderId"; Pass the DataTable in your Dapper query call sqlConn.Query(query, new {tvp = tvpTable.AsTableValuedParameter("dbo.MyTVP")});

当您想要对多个列进行大规模更新时,这也非常有效——只需构建一个TVP,并使用到TVP的内部连接执行update。

其他回答

还要确保你没有像这样用圆括号括起你的查询字符串:

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

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

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

如果你的IN子句太大,MSSQL无法处理,你可以很容易地使用一个TableValueParameter与Dapper。

Create your TVP type in MSSQL: CREATE TYPE [dbo].[MyTVP] AS TABLE([ProviderId] [int] NOT NULL) Create a DataTable with the same column(s) as the TVP and populate it with values var tvpTable = new DataTable(); tvpTable.Columns.Add(new DataColumn("ProviderId", typeof(int))); // fill the data table however you wish Modify your Dapper query to do an INNER JOIN on the TVP table: var query = @"SELECT * FROM Providers P INNER JOIN @tvp t ON p.ProviderId = t.ProviderId"; Pass the DataTable in your Dapper query call sqlConn.Query(query, new {tvp = tvpTable.AsTableValuedParameter("dbo.MyTVP")});

当您想要对多个列进行大规模更新时,这也非常有效——只需构建一个TVP,并使用到TVP的内部连接执行update。

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

在网络上有很多可用的分配器函数,如果你喜欢SQL,你很容易就能找到一个。

然后你可以做…

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

Or

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

(或相似的)

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

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

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

直接从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