C#使用SQLBulkCopy或等效库高效批量删除50000条记录
Posted
技术标签:
【中文标题】C#使用SQLBulkCopy或等效库高效批量删除50000条记录【英文标题】:C# Efficiently delete 50000 records in batches using SQLBulkCopy or equivalent library 【发布时间】:2019-06-21 13:42:02 【问题描述】:我正在使用这个库来批量执行批量删除,如下所示:
while (castedEndedItems.Any())
var subList = castedEndedItems.Take(4000).ToList();
DBRetry.Do(() => EFBatchOperation.For(ctx, ctx.SearchedUserItems).Where(r => subList.Any(a => a == r.ItemID)).Delete(), TimeSpan.FromSeconds(2));
castedEndedItems.RemoveRange(0, subList.Count);
Console.WriteLine("Completed a batch of ended items");
正如你们所见,我一次性删除了一批 4000 个项目,并将它们作为参数传递给查询...
我正在使用这个库来执行批量删除:
https://github.com/MikaelEliasson/EntityFramework.Utilities
但是这样的性能绝对是糟糕的......我测试了几次应用程序并删除了 80000 条记录,例如它需要 40 分钟!?
我应该注意,我删除的那个参数 (ItemID) 是 varchar(400) 类型的,并且出于性能原因它被索引......
是否有任何其他库我可以使用或调整此查询以使其更快地工作,因为目前的性能绝对糟糕..:/
【问题讨论】:
您要删除所有内容吗?还是只是行的一个子集? (即truncate table
是一个选项吗?)
使用带有实体框架原始查询的 SQL 语句。有关示例,请参阅here。性能应该几乎是即时的。
@MarcGravell no 如你所见,我有一个列表,其中只有匹配的项目必须通过名为“ItemID”的参数删除 - 这是 varchar(250) 类型:)
@RobertHarvey 我认为 SQLBulkCompy 库的实现会更好,不是吗? SQLBulkCopy 是为这些类型的东西制作的。我只是找不到任何具有良好实现的东西^^
您是在批量复制还是批量删除?
【参考方案1】:
如果您准备使用存储过程,那么您可以在没有任何外部库的情况下执行此操作:
使用表值参数@ids
创建存储过程
为该表值参数定义一个 SQL 类型(假设一个简单的 PK 只是一个 id
列)
在存储过程中使用
delete from table where id in (select id from @ids);
在您的应用程序中创建一个 DataTable
并填充以匹配 SQL 表
This answer 说明了这个过程。
任何其他选项都需要做同样的事情——或者效率较低的事情。
【讨论】:
您可以通过这种方式传递的 id 数量可能会受到限制。 @RobertHarvey TVP 可以有任意数量的行;不过,我个人可能仍然会循环,因为 huge 批次可能有 trx 问题 @RobertHarvey 是的,但它远远超过这里讨论的数字(可能受到 SQL Server 批处理大小的限制......)。如果这是一个问题,那么一次做 10,000 个 :-)【参考方案2】:这里的任何 EF 解决方案都可能会执行 很多 谨慎的操作。相反,我建议在循环中手动构建您的 SQL,例如:
using(var cmd = db.CreateCommand())
int index = 0;
var sql = new StringBuilder("delete from [SomeTable] where [SomeId] in (");
foreach(var item in items)
if (index != 0) sql.Append(',');
var name = "@id_" + index++;
sql.Append(name);
cmd.Parameters.AddWithValue(name, item.SomeId);
cmd.CommandText = sql.Append(");").ToString();
cmd.ExecuteNonQuery();
您可能需要分批循环 this,因为命令中允许的参数数量有上限。
【讨论】:
Marc thx 的回复,我总是更喜欢干净的 SQL 语句而不是 EF 来执行批量删除,因为它更好......你认为删除 50000 条记录需要多少时间n个表,总共有1.8亿条记录? @User987 太多取决于记录的大小、PK 是否聚集、存在哪些其他索引(它们的条目也需要删除。以及您拥有什么样的硬件, P.S.我如何匹配项目的方式是通过类型为“Varchar”的 ItemID ...我想这会导致性能瓶颈? @Marc Gravell 所以我猜这会比 SQLBulkCopy 更好? @Marc Gravell 我尝试使用这段代码,但它说例如:上下文中不存在“CreateCommand”...【参考方案3】:如果你不介意额外的依赖,你可以使用 NuGet 包 Z.EntityFramework.Plus。
代码大致如下:
using Z.EntityFramework.Plus;
[...]
using (yourDbContext context = new yourDbContext())
yourDbContext.yourDbSet.Where( yourWhereExpression ).Delete();
简单高效。 documentation 包含有关性能的确切数字。
关于许可:据我所知,1.8 版有 MIT 许可:https://github.com/zzzprojects/EntityFramework-Plus/blob/master/LICENSE 较新的版本不能免费使用。
【讨论】:
我知道这个,但这是商业图书馆,它不是免费的......它非常昂贵:) 据我所知,有一个有 MIT 许可证:github.com/zzzprojects/EntityFramework-Plus/blob/master/LICENSE 真的等吗?从什么时候开始他们将其更改为 MIT 许可证:O 天哪,它真的是 MIT 许可证。我读过很多关于那个图书馆的书,它太棒了...我不知道他们现在免费提供它?o.O 我认为是旧版本,但是 1.8 版本的 NuGet 包链接了这个许可证。项目页面的新版本是3.1...所以1.8可能没有最好的性能和最酷的代码风格,但可能值得一试。以上是关于C#使用SQLBulkCopy或等效库高效批量删除50000条记录的主要内容,如果未能解决你的问题,请参考以下文章