C#中如何执行数据库的批量更新?使用SqlBulkCopy?

C#中如何执行数据库的批量更新?使用SqlBulkCopy?
最新回答
风铃鹿

2023-07-04 06:15:51

在C#中执行数据库批量更新时,SqlBulkCopy仅支持批量插入,无法直接完成UPDATE操作。以下是几种高效实现批量更新的方法,结合SQL Server特性与C#代码示例:

1. 表值参数(TVP)+ MERGE语句(推荐)

原理:通过用户定义表类型(UDT)将数据批量传入存储过程,利用MERGE语句实现“存在则更新,不存在则插入”。步骤

  • SQL Server端

    -- 创建表类型CREATE TYPE dbo.UserUpdateType AS TABLE ( Id INT PRIMARY KEY, Name NVARCHAR(50), Email NVARCHAR(100));-- 创建存储过程CREATE PROCEDURE UpdateUsers (@updates dbo.UserUpdateType READONLY)ASMERGE Users AS targetUSING @updates AS sourceON target.Id = source.IdWHEN MATCHED THEN UPDATE SET Name = source.Name, Email = source.Email;
  • C#端

    var table = new DataTable();table.Columns.Add("Id", typeof(int));table.Columns.Add("Name", typeof(string));table.Columns.Add("Email", typeof(string));// 添加数据table.Rows.Add(1, "张三", "zhang@example.com");table.Rows.Add(2, "李四", "li@example.com");using (var conn = new SqlConnection(connectionString)) { conn.Open(); using (var cmd = new SqlCommand("UpdateUsers", conn)) { cmd.CommandType = CommandType.StoredProcedure; var param = cmd.Parameters.AddWithValue("@updates", table); param.SqlDbType = SqlDbType.Structured; param.TypeName = "dbo.UserUpdateType"; cmd.ExecuteNonQuery(); }}

优势:单次数据库往返,支持复杂逻辑,适合大数据量。

2. 使用Entity Framework扩展库

EF Core原生通过SaveChanges()逐条提交,性能较差。推荐以下扩展库:

  • Z.EntityFramework.Extensions(商业库):context.Users .Where(u => u.Status == 0) .Update(u => new User { Status = 1 });
  • EFCore.BulkExtensions(开源免费):var updates = new List<User> { new User { Id = 1, Name = "张三" }, new User { Id = 2, Name = "李四" }};context.BulkUpdate(updates);

适用场景:已使用EF Core的项目,需快速集成批量操作。

3. 拼接SQL批量语句(小批量适用)

通过CASE WHEN或参数化查询拼接UPDATE语句,适合几百条数据:

var ids = new[] { 1, 2 };var names = new[] { "张三", "李四" };var emails = new[] { "zhang@ex.com", "li@ex.com" };var sql = @"UPDATE Users SET Name = CASE Id WHEN @id1 THEN @name1 WHEN @id2 THEN @name2 END, Email = CASE Id WHEN @id1 THEN @email1 WHEN @id2 THEN @email2 END WHERE Id IN (@id1, @id2)";using (var conn = new SqlConnection(connectionString)) { conn.Open(); using (var cmd = new SqlCommand(sql, conn)) { cmd.Parameters.AddWithValue("@id1", ids[0]); cmd.Parameters.AddWithValue("@id2", ids[1]); cmd.Parameters.AddWithValue("@name1", names[0]); cmd.Parameters.AddWithValue("@name2", names[1]); cmd.Parameters.AddWithValue("@email1", emails[0]); cmd.Parameters.AddWithValue("@email2", emails[1]); cmd.ExecuteNonQuery(); }}

注意:SQL语句长度受限,大数据量易出错。

4. 临时表 + UPDATE JOIN

结合SqlBulkCopy的高速写入与SQL的灵活更新:

// 1. 创建临时表using (var conn = new SqlConnection(connectionString)) { conn.Open(); using (var cmd = new SqlCommand(@" CREATE TABLE #TempUpdates (Id INT, Name NVARCHAR(50), Email NVARCHAR(100))", conn)) { cmd.ExecuteNonQuery(); } // 2. 用SqlBulkCopy写入临时表 var tempTable = new DataTable(); tempTable.Columns.Add("Id", typeof(int)); tempTable.Columns.Add("Name", typeof(string)); tempTable.Columns.Add("Email", typeof(string)); tempTable.Rows.Add(1, "张三", "zhang@ex.com"); tempTable.Rows.Add(2, "李四", "li@ex.com"); using (var bulkCopy = new SqlBulkCopy(conn)) { bulkCopy.DestinationTableName = "#TempUpdates"; bulkCopy.WriteToServer(tempTable); } // 3. 执行UPDATE JOIN using (var cmd = new SqlCommand(@" UPDATE u SET u.Name = t.Name, u.Email = t.Email FROM Users u INNER JOIN #TempUpdates t ON u.Id = t.Id", conn)) { cmd.ExecuteNonQuery(); }}

优势:利用SqlBulkCopy快速导入,适合超大数据量。

总结建议
  • 首选方案:表值参数 + MERGE语句,兼顾性能与可维护性。
  • EF项目:使用EFCore.BulkExtensions等扩展库。
  • 简单场景:小批量数据可用拼接SQL或临时表方案。
  • 避免:直接使用SqlBulkCopy进行更新,或循环单条UPDATE语句。