C# 操作 SQL Server 批量更新 完整指南
你需要在 C# 中实现 SQL Server 的批量更新功能,不同数据量场景对应不同最优方案(小数据量简洁高效、大数据量高性能低损耗),下面详细讲解每种方案的实现代码、适用场景及注意事项。
一、 小数据量批量更新(1000 条以内,简洁高效)
适用于更新数据量较少(如几百条)的场景,实现简单、无需额外依赖,核心分为「拼接 SQL 语句」和「参数化 SQL(推荐,防注入)」两种方式。
方案 1:参数化 SQL 拼接(防 SQL 注入,推荐)
通过拼接带参数的 UPDATE 语句,批量执行更新,兼顾简洁性和安全性,避免 SQL 注入风险。
实现代码
csharp
运行
using System;
using System.Data.SqlClient;
namespace SqlServerBatchUpdate
{
class SmallDataBatchUpdate
{
// 数据库连接字符串(根据你的实际环境修改)
private static readonly string _connectionString = "Data Source=你的服务器名;Initial Catalog=你的数据库名;User ID=你的用户名;Password=你的密码;";
/// <summary>
/// 批量更新员工薪资(小数据量)
/// </summary>
public static void BatchUpdateEmployeeSalary()
{
// 模拟需要更新的数据(实际可从业务逻辑中获取)
var updateList = new List<Employee>()
{
new Employee { EmployeeID = 1, Salary = 5500 },
new Employee { EmployeeID = 2, Salary = 6600 },
new Employee { EmployeeID = 3, Salary = 7700 }
};
// 拼接参数化SQL语句
var sqlBuilder = new System.Text.StringBuilder();
var sqlParameters = new List<SqlParameter>();
for (int i = 0; i < updateList.Count; i++)
{
var emp = updateList[i];
// 定义唯一参数名,避免冲突
string paramIdName = $"@EmpID{i}";
string paramSalaryName = $"@EmpSalary{i}";
// 拼接UPDATE语句(每条数据对应一个UPDATE,或用CASE WHEN优化)
sqlBuilder.AppendLine($"UPDATE Employee SET Salary = {paramSalaryName} WHERE EmployeeID = {paramIdName};");
// 添加参数
sqlParameters.Add(new SqlParameter(paramIdName, emp.EmployeeID));
sqlParameters.Add(new SqlParameter(paramSalaryName, emp.Salary));
}
try
{
using (SqlConnection conn = new SqlConnection(_connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sqlBuilder.ToString(), conn))
{
// 添加所有参数
cmd.Parameters.AddRange(sqlParameters.ToArray());
// 执行批量更新
int affectedRows = cmd.ExecuteNonQuery();
Console.WriteLine($"成功更新 {affectedRows} 条记录");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"批量更新失败:{ex.Message}");
}
}
}
// 员工实体类
public class Employee
{
public int EmployeeID { get; set; }
public decimal Salary { get; set; }
public string EmployeeName { get; set; }
public string Department { get; set; }
}
}
优化:CASE WHEN 减少 SQL 语句数
上述代码每条数据对应一个 UPDATE,可通过 CASE WHEN 优化为单条 UPDATE,提升执行效率:
csharp
运行
public static void BatchUpdateEmployeeSalaryWithCaseWhen()
{
var updateList = new List<Employee>()
{
new Employee { EmployeeID = 1, Salary = 5500 },
new Employee { EmployeeID = 2, Salary = 6600 },
new Employee { EmployeeID = 3, Salary = 7700 }
};
var caseBuilder = new System.Text.StringBuilder();
var idList = new List<int>();
var sqlParameters = new List<SqlParameter>();
// 拼接CASE WHEN语句
caseBuilder.Append("UPDATE Employee SET Salary = CASE EmployeeID ");
for (int i = 0; i < updateList.Count; i++)
{
var emp = updateList[i];
string paramIdName = $"@EmpID{i}";
string paramSalaryName = $"@EmpSalary{i}";
caseBuilder.AppendLine($"WHEN {paramIdName} THEN {paramSalaryName} ");
sqlParameters.Add(new SqlParameter(paramIdName, emp.EmployeeID));
sqlParameters.Add(new SqlParameter(paramSalaryName, emp.Salary));
idList.Add(emp.EmployeeID);
}
caseBuilder.Append("END ");
// 拼接WHERE条件,限定更新范围
caseBuilder.Append("WHERE EmployeeID IN (");
caseBuilder.Append(string.Join(",", idList));
caseBuilder.Append(");");
try
{
using (SqlConnection conn = new SqlConnection(_connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(caseBuilder.ToString(), conn))
{
cmd.Parameters.AddRange(sqlParameters.ToArray());
int affectedRows = cmd.ExecuteNonQuery();
Console.WriteLine($"成功更新 {affectedRows} 条记录");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"批量更新失败:{ex.Message}");
}
}
方案 2:循环单条更新(最简单,不推荐大数据量)
逐行遍历数据,执行单条 UPDATE 语句,实现最简单,但性能较差(多次数据库连接 / 交互),仅适用于极少数据(几十条以内)。
实现代码
csharp
运行
public static void LoopSingleUpdate()
{
var updateList = new List<Employee>()
{
new Employee { EmployeeID = 1, Salary = 5500 },
new Employee { EmployeeID = 2, Salary = 6600 }
};
string sql = "UPDATE Employee SET Salary = @Salary WHERE EmployeeID = @EmployeeID;";
try
{
using (SqlConnection conn = new SqlConnection(_connectionString))
{
conn.Open();
foreach (var emp in updateList)
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@Salary", emp.Salary);
cmd.Parameters.AddWithValue("@EmployeeID", emp.EmployeeID);
cmd.ExecuteNonQuery();
}
}
Console.WriteLine("全部更新完成");
}
}
catch (Exception ex)
{
Console.WriteLine($"更新失败:{ex.Message}");
}
}
二、 大数据量批量更新(1000 条以上,高性能)
当更新数据量较大(如 1 万、10 万甚至百万级)时,上述小数据量方案会出现性能瓶颈(多次交互、事务日志暴涨),推荐以下两种高性能方案。
方案 1:使用 SqlBulkCopy + 临时表(最优推荐,超高效率)
核心思路:
- 将需要更新的数据先通过
SqlBulkCopy批量插入 SQL Server 临时表; - 通过
JOIN语法,将临时表与目标表关联,执行批量更新; - 删除临时表(可选,自动临时表会自动销毁)。
该方案最大限度减少数据库交互,利用 SqlBulkCopy 的高性能批量写入能力,是大数据量更新的首选。
实现代码
csharp
运行
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
namespace SqlServerBatchUpdate
{
class BigDataBatchUpdate
{
private static readonly string _connectionString = "Data Source=你的服务器名;Initial Catalog=你的数据库名;User ID=你的用户名;Password=你的密码;";
/// <summary>
/// 大数据量批量更新(SqlBulkCopy + 临时表)
/// </summary>
public static void BatchUpdateWithBulkCopy()
{
// 模拟10000条需要更新的数据(实际可从业务中获取)
var updateList = new List<Employee>();
for (int i = 1; i <= 10000; i++)
{
updateList.Add(new Employee { EmployeeID = i, Salary = 5000 + i * 10 });
}
// 1. 将List转换为DataTable(SqlBulkCopy支持DataTable入参)
DataTable tempDt = ConvertListToDataTable(updateList);
try
{
using (SqlConnection conn = new SqlConnection(_connectionString))
{
conn.Open();
// 开启事务,确保操作原子性
using (SqlTransaction tran = conn.BeginTransaction())
{
try
{
// 2. 创建临时表(#开头为局部临时表,仅当前连接可见)
string createTempTableSql = @"
CREATE TABLE #TempEmployee (
EmployeeID INT PRIMARY KEY,
Salary DECIMAL(18, 2) NOT NULL
);";
using (SqlCommand cmd = new SqlCommand(createTempTableSql, conn, tran))
{
cmd.ExecuteNonQuery();
}
// 3. SqlBulkCopy 批量插入临时表
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, tran))
{
bulkCopy.DestinationTableName = "#TempEmployee"; // 目标临时表名
bulkCopy.BatchSize = 1000; // 每批次插入1000条
bulkCopy.BulkCopyTimeout = 30; // 超时时间30秒
// 映射DataTable列与临时表列(列名一致可省略,建议显式映射)
bulkCopy.ColumnMappings.Add("EmployeeID", "EmployeeID");
bulkCopy.ColumnMappings.Add("Salary", "Salary");
// 执行批量插入
bulkCopy.WriteToServer(tempDt);
}
// 4. 关联临时表与目标表,执行批量更新
string batchUpdateSql = @"
UPDATE E
SET E.Salary = T.Salary,
E.UpdateTime = GETDATE()
FROM Employee E
INNER JOIN #TempEmployee T
ON E.EmployeeID = T.EmployeeID;";
using (SqlCommand cmd = new SqlCommand(batchUpdateSql, conn, tran))
{
int affectedRows = cmd.ExecuteNonQuery();
Console.WriteLine($"成功更新 {affectedRows} 条记录");
}
// 5. 删除临时表(可选,局部临时表连接关闭后自动销毁)
string dropTempTableSql = "DROP TABLE #TempEmployee;";
using (SqlCommand cmd = new SqlCommand(dropTempTableSql, conn, tran))
{
cmd.ExecuteNonQuery();
}
// 提交事务
tran.Commit();
}
catch (Exception ex)
{
// 出错回滚事务
tran.Rollback();
Console.WriteLine($"批量更新失败:{ex.Message}");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"数据库连接失败:{ex.Message}");
}
}
/// <summary>
/// List转DataTable(通用方法)
/// </summary>
private static DataTable ConvertListToDataTable<T>(List<T> list)
{
DataTable dt = new DataTable();
var properties = typeof(T).GetProperties();
// 添加DataTable列
foreach (var prop in properties)
{
dt.Columns.Add(prop.Name, prop.PropertyType);
}
// 填充DataTable数据
foreach (var item in list)
{
DataRow row = dt.NewRow();
foreach (var prop in properties)
{
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
}
dt.Rows.Add(row);
}
return dt;
}
}
}
方案 2:使用 Dapper 框架(简洁高效,第三方库)
Dapper 是轻量级 ORM 框架,简化数据库操作,支持批量更新,通过拼接参数化 SQL 或使用 Execute 方法批量执行,兼顾简洁性和性能,需先安装 Dapper 包。
步骤 1:安装 Dapper 包
通过 NuGet 安装:Install-Package Dapper 或 dotnet add package Dapper
实现代码
csharp
运行
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using Dapper;
namespace SqlServerBatchUpdate
{
class DapperBatchUpdate
{
private static readonly string _connectionString = "Data Source=你的服务器名;Initial Catalog=你的数据库名;User ID=你的用户名;Password=你的密码;";
/// <summary>
/// Dapper 批量更新
/// </summary>
public static void BatchUpdateWithDapper()
{
var updateList = new List<Employee>()
{
new Employee { EmployeeID = 1, Salary = 5500 },
new Employee { EmployeeID = 2, Salary = 6600 },
new Employee { EmployeeID = 3, Salary = 7700 }
};
try
{
using (SqlConnection conn = new SqlConnection(_connectionString))
{
conn.Open();
// 批量执行更新(Dapper自动处理参数)
string sql = "UPDATE Employee SET Salary = @Salary WHERE EmployeeID = @EmployeeID;";
int affectedRows = conn.Execute(sql, updateList);
Console.WriteLine($"成功更新 {affectedRows} 条记录");
}
}
catch (Exception ex)
{
Console.WriteLine($"Dapper批量更新失败:{ex.Message}");
}
}
}
}
三、 关键注意事项(避坑指南)
- 防 SQL 注入 :始终使用「参数化 SQL」(避免直接拼接字符串),无论是原生 ADO.NET 还是 Dapper,参数化查询是防止 SQL 注入的核心。
- 事务保护 :批量更新属于关键操作,建议开启事务(
SqlTransaction),确保所有更新要么全部成功,要么全部回滚,避免数据不一致。 - 连接释放 :使用
using语句包裹SqlConnection、SqlCommand等对象,自动释放数据库连接资源,避免连接泄露。 - 批量大小控制 :使用
SqlBulkCopy时,合理设置BatchSize(建议 1000~5000),过大可能占用过多内存,过小会降低效率。 - 索引优化 :目标表的关联字段(如
EmployeeID)建议建立主键或索引,提升JOIN操作和WHERE筛选的效率。 - 超时设置 :大数据量更新时,适当调整
CommandTimeout(默认 30 秒),避免因执行时间过长导致超时。
总结
- 小数据量(<1000 条) :优先使用「参数化 SQL + CASE WHEN」(原生 ADO.NET)或 Dapper,简洁高效。
- 大数据量(≥1000 条):首选「SqlBulkCopy + 临时表」,性能最优,最大限度减少数据库交互。
- 开发效率优先:使用 Dapper 框架,简化代码编写,兼顾性能和可读性。
- 数据安全优先:开启事务保护、使用参数化查询、合理释放连接资源。