从C#向SQL Server快速插入记录及多表单事务删插操作咨询
从C#向SQL Server插入记录的最快方式及多表事务操作实现
一、最快插入方式:SqlBulkCopy
毫无疑问,SqlBulkCopy是C#里向SQL Server批量插数据的最优解,它专门针对批量数据传输做了优化,比循环跑单条INSERT语句快好几个量级——核心是直接把DataTable、DataReader或者强类型集合的数据批量写入数据库,大幅减少了网络往返的开销。
给你个简单示例(假设你有个User类对应数据库表):
using (var connection = new SqlConnection("你的数据库连接字符串")) { connection.Open(); // 准备插入数据,这里用DataTable举例,也可以用自定义DataReader var dataTable = new DataTable(); dataTable.Columns.Add("Id", typeof(int)); dataTable.Columns.Add("Name", typeof(string)); // 填充100条测试数据 for (int i = 0; i < 100; i++) { dataTable.Rows.Add(i, $"用户_{i}"); } using (var bulkCopy = new SqlBulkCopy(connection)) { bulkCopy.DestinationTableName = "Users"; // 目标表名 // 如果DataTable列名和数据库表列名完全一致,这步映射可以省略 bulkCopy.ColumnMappings.Add("Id", "Id"); bulkCopy.ColumnMappings.Add("Name", "UserName"); bulkCopy.WriteToServer(dataTable); } }
如果你的数据是强类型集合,还可以用FastMember这类工具快速把IEnumerable<T>转成DataReader,省去手动构建DataTable的麻烦,性能还能再提一截。
二、单事务内完成5张表的删数+插入操作
要让所有操作要么全成要么全滚,关键是用SqlTransaction把所有删除、插入步骤包起来。结合SqlBulkCopy的实现步骤如下:
- 打开数据库连接,启动事务;
- 逐个表执行DELETE清空数据;
- 逐个表执行批量插入;
- 所有操作无异常就提交事务,出问题就回滚。
示例代码如下:
string connectionString = "你的数据库连接字符串"; // 假设要操作的5张表名 List<string> targetTables = new List<string> { "TableA", "TableB", "TableC", "TableD", "TableE" }; using (var connection = new SqlConnection(connectionString)) { connection.Open(); // 开启事务 using (var transaction = connection.BeginTransaction()) { try { foreach (var tableName in targetTables) { // 第一步:清空当前表数据 string deleteSql = $"DELETE FROM {tableName}"; using (var deleteCmd = new SqlCommand(deleteSql, connection, transaction)) { deleteCmd.ExecuteNonQuery(); } // 第二步:批量插入100条数据到当前表 // 这里用自定义方法生成对应表的DataTable,实际项目里可以根据业务逻辑生成数据 var tableData = GenerateTableData(tableName); using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction)) { bulkCopy.DestinationTableName = tableName; // 列名一致的话不用手动映射,否则添加ColumnMappings bulkCopy.WriteToServer(tableData); } } // 所有操作成功,提交事务 transaction.Commit(); Console.WriteLine("所有表的清空和插入操作已完成!"); } catch (Exception ex) { // 出错就回滚所有操作 transaction.Rollback(); Console.WriteLine($"操作失败,已回滚:{ex.Message}"); throw; // 可以根据业务需求决定是否抛出异常 } } } // 辅助方法:根据表名生成对应结构的DataTable(示例) private static DataTable GenerateTableData(string tableName) { var dt = new DataTable(); // 根据不同表名定义对应列,要和数据库表结构匹配 switch (tableName) { case "TableA": dt.Columns.Add("Id", typeof(int)); dt.Columns.Add("Content", typeof(string)); break; case "TableB": dt.Columns.Add("ProductId", typeof(int)); dt.Columns.Add("Price", typeof(decimal)); break; // 其他表的列定义... } // 填充100条测试数据 for (int i = 0; i < 100; i++) { var row = dt.NewRow(); if (tableName == "TableA") { row["Id"] = i; row["Content"] = $"测试内容_{i}"; } else if (tableName == "TableB") { row["ProductId"] = i; row["Price"] = i * 2.5m; } // 其他表的数据填充逻辑... dt.Rows.Add(row); } return dt; }
几点注意事项:
- 确保连接字符串开启了连接池(默认是开的),避免频繁创建连接的开销;
- 如果表有主键或唯一索引,插入前要保证数据不会冲突;
- 批量插入时如果表有触发器,可能拖慢性能,必要时可以临时禁用触发器(操作完再开启);
- 这个场景下事务范围不大,不会超时,但如果是超大量数据,要记得调整事务超时时间。
内容的提问来源于stack exchange,提问作者Syed Qayyum




