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条记录的主要内容,如果未能解决你的问题,请参考以下文章

大数据处理高效能,大数据量存储方案SqlBulkCopy

C# 中的 SqlBulkCopy 类正在更改数值

如何使用c#批量新增数据oracledataaccess

实体框架和经典 Ado.net 之间的单个事务或批量插入操作下的 SqlBulkCopy 多个表插入

使用 SqlBulkCopy 连接到 SQLite 数据库

SqlBulkCopy 的推荐批量大小是多少?