gpt4 book ai didi

c# - 如何使用 Dapper 在 IN 子句中使用超过 2100 个值?

转载 作者:太空狗 更新时间:2023-10-29 21:26:38 24 4
gpt4 key购买 nike

我有一个包含 id 的列表,我想使用 Dapper 将其插入到临时表中,以避免 SQL 对“IN”子句中参数的限制。

所以目前我的代码是这样的:

public IList<int> LoadAnimalTypeIdsFromAnimalIds(IList<int> animalIds)
{
using (var db = new SqlConnection(this.connectionString))
{
return db.Query<int>(
@"SELECT a.animalID
FROM
dbo.animalTypes [at]
INNER JOIN animals [a] on a.animalTypeId = at.animalTypeId
INNER JOIN edibleAnimals e on e.animalID = a.animalID
WHERE
at.animalId in @animalIds", new { animalIds }).ToList();
}
}

我需要解决的问题是,当animalIds列表中的id超过2100个时,我会得到一个SQL错误“传入的请求参数太多。服务器最多支持2100个参数”。

所以现在我想创建一个临时表,其中填充了传递给方法的 animalIds。然后我可以加入临时表上的动物表,避免有一个巨大的“IN”子句。

我尝试了各种语法组合,但没有成功。这就是我现在所在的位置:

public IList<int> LoadAnimalTypeIdsFromAnimalIds(IList<int> animalIds)
{
using (var db = new SqlConnection(this.connectionString))
{
db.Execute(@"SELECT INTO #tempAnmialIds @animalIds");

return db.Query<int>(
@"SELECT a.animalID
FROM
dbo.animalTypes [at]
INNER JOIN animals [a] on a.animalTypeId = at.animalTypeId
INNER JOIN edibleAnimals e on e.animalID = a.animalID
INNER JOIN #tempAnmialIds tmp on tmp.animalID = a.animalID).ToList();
}
}

我无法让 SELECT INTO 使用 ID 列表。我是否以错误的方式解决这个问题,也许有更好的方法来避免“IN”子句限制。

我确实有一个备份解决方案,因为我可以将传入的 animalID 列表拆分为 1000 个 block ,但我读到大型“IN”子句会受到性能影响并且加入临时表会更有效率并且它也意味着我不需要额外的“拆分”代码来将 ID 批量分成 1000 个 block 。

最佳答案

好的,这是您想要的版本。我将其添加为单独的答案,因为我使用 SP/TVP 的第一个答案使用了不同的概念。

public IList<int> LoadAnimalTypeIdsFromAnimalIds(IList<int> animalIds)
{
using (var db = new SqlConnection(this.connectionString))
{
// This Open() call is vital! If you don't open the connection, Dapper will
// open/close it automagically, which means that you'll loose the created
// temp table directly after the statement completes.
db.Open();

// This temp table is created having a primary key. So make sure you don't pass
// any duplicate IDs
db.Execute("CREATE TABLE #tempAnimalIds(animalId int not null primary key);");
while (animalIds.Any())
{
// Build the statements to insert the Ids. For this, we need to split animalIDs
// into chunks of 1000, as this flavour of INSERT INTO is limited to 1000 values
// at a time.
var ids2Insert = animalIds.Take(1000);
animalIds = animalIds.Skip(1000).ToList();

StringBuilder stmt = new StringBuilder("INSERT INTO #tempAnimalIds VALUES (");
stmt.Append(string.Join("),(", ids2Insert));
stmt.Append(");");

db.Execute(stmt.ToString());
}

return db.Query<int>(@"SELECT animalID FROM #tempAnimalIds").ToList();
}
}

测试:

var ids = LoadAnimalTypeIdsFromAnimalIds(Enumerable.Range(1, 2500).ToList());

您只需将选择语句修改为原来的样子。由于我的环境中没有您的所有表,我只是从创建的临时表中进行选择以证明它按应有的方式工作。

陷阱,见评论:

  • 一开始就打开连接,否则临时表会在 dapper 自动关闭连接后立即消失创建表格。
  • INSERT INTO 的这种特殊 flavor 是有限的一次最多 1000 个值,因此传递的 ID 需要拆分为相应地分块。
  • 不要传递重复键,因为临时表上的主键不允许这样做。

编辑

看起来 Dapper 支持基于集合的操作,这也将使这项工作有效:

public IList<int> LoadAnimalTypeIdsFromAnimalIdsV2(IList<int> animalIds)
{
// This creates an IEnumerable of an anonymous type containing an Id property. This seems
// to be necessary to be able to grab the Id by it's name via Dapper.
var namedIDs = animalIds.Select(i => new {Id = i});
using (var db = new SqlConnection(this.connectionString))
{
// This is vital! If you don't open the connection, Dapper will open/close it
// automagically, which means that you'll loose the created temp table directly
// after the statement completes.
db.Open();

// This temp table is created having a primary key. So make sure you don't pass
// any duplicate IDs
db.Execute("CREATE TABLE #tempAnimalIds(animalId int not null primary key);");

// Using one of Dapper's convenient features, the INSERT becomes:
db.Execute("INSERT INTO #tempAnimalIds VALUES(@Id);", namedIDs);

return db.Query<int>(@"SELECT animalID FROM #tempAnimalIds").ToList();
}
}

我不知道与以前的版本(即 2500 个单插入,而不是三个分别具有 1000、1000、500 个值的插入)相比,它的性能如何。但文档建议,如果与异步、MARS 和流水线一起使用,它的性能会更好。

关于c# - 如何使用 Dapper 在 IN 子句中使用超过 2100 个值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39592340/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com