使用SqlBulkCopy将CSV导入SQL Server多表遇阻求助
解决SqlBulkCopy多表批量导入的问题
嘿,先瞅你代码里几个核心问题,这应该就是你多表导入失败的根源,咱们一步步来梳理和修改:
1. 缺失CSV数据读取与表拆分逻辑
你只初始化了TextFieldParser,但完全没写读取CSV行的代码,而且Table001和Table002这两个变量根本没定义!你得先把CSV里的每一行读出来,再把对应字段分配到目标表的数据行里,这是多表导入的核心步骤。
2. 事务未添加异常回滚机制
现在的代码如果其中一个表的导入失败,事务不会回滚,会导致部分表导入成功、部分失败,还可能让数据库连接处于异常状态。必须加try-catch块来处理回滚,保证数据一致性。
3. 百万级数据的优化建议
- 你的
BatchSize设成99999有点太大了,建议改成10000-50000之间,避免内存占用过高引发溢出。 - 如果不需要保持CSV里的自增ID值,可以去掉
SqlBulkCopyOptions.KeepIdentity,减少数据库的额外处理开销。 - 可以开启
SqlBulkCopyOptions.TableLock,批量导入时锁定目标表,能显著提升导入速度(注意这会暂时影响其他对该表的操作)。
下面是修改后的完整代码示例,我把数据读取、表拆分、事务回滚、分批写入这些关键逻辑都补上了:
namespace SQLBulkDT2012 { public class CsvBulkCopyDataIntoSqlServer { public static void Main(string[] args) { try { LoadCsvDataIntoSqlServer(); Console.WriteLine("Done!"); } catch (Exception ex) { Console.WriteLine($"导入失败:{ex.Message}"); } Console.ReadKey(); } static string GetPath() { return Path.Combine(Environment.CurrentDirectory, "test.csv"); } static void LoadCsvDataIntoSqlServer() { string filePath = GetPath(); var connectionString = ConfigurationManager.ConnectionStrings["SQLBulkDT2012.Properties.Settings.CMSConnectionString"].ConnectionString; // 初始化两个目标表的结构(和数据库表列名、类型对应) var table001 = new DataTable("SY001_tst"); table001.Columns.Add("COLUMN_001"); table001.Columns.Add("COLUMN_002"); var table002 = new DataTable("SY002_tst"); table002.Columns.Add("COLUMN_003"); table002.Columns.Add("COLUMN_004"); // 读取CSV数据并填充到对应表 using (var textFieldParser = new TextFieldParser(filePath)) { textFieldParser.TextFieldType = FieldType.Delimited; textFieldParser.Delimiters = new[] { "," }; textFieldParser.HasFieldsEnclosedInQuotes = true; // 跳过CSV表头(如果你的文件有表头的话,没有就注释掉这行) textFieldParser.ReadFields(); string[] fields; while ((fields = textFieldParser.ReadFields()) != null) { // 填充SY001_tst的数据行 var row001 = table001.NewRow(); row001["COLUMN_001"] = fields[0]; // 对应CSV第一列 row001["COLUMN_002"] = fields[1]; // 对应CSV第二列 table001.Rows.Add(row001); // 填充SY002_tst的数据行 var row002 = table002.NewRow(); row002["COLUMN_003"] = fields[2]; // 对应CSV第三列 row002["COLUMN_004"] = fields[3]; // 对应CSV第四列 table002.Rows.Add(row002); // 达到批次大小就批量写入,避免内存溢出 if (table001.Rows.Count >= 10000) { WriteTableToSql(table001, "SY001_tst", connectionString); table001.Clear(); } if (table002.Rows.Count >= 10000) { WriteTableToSql(table002, "SY002_tst", connectionString); table002.Clear(); } } // 写入最后一批剩余的数据 if (table001.Rows.Count > 0) { WriteTableToSql(table001, "SY001_tst", connectionString); } if (table002.Rows.Count > 0) { WriteTableToSql(table002, "SY002_tst", connectionString); } } } // 封装通用的批量写入方法,减少重复代码 static void WriteTableToSql(DataTable dataTable, string destinationTable, string connectionString) { using (var sqlConnection = new SqlConnection(connectionString)) { sqlConnection.Open(); using (var transaction = sqlConnection.BeginTransaction()) { try { // 加上TableLock提升导入速度,不需要的话可以去掉 using (var sqlBulkCopy = new SqlBulkCopy(sqlConnection, SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.TableLock, transaction)) { sqlBulkCopy.BatchSize = 10000; sqlBulkCopy.DestinationTableName = destinationTable; // 自动匹配列(如果CSV和数据库列名完全一致,不用手动写映射) foreach (DataColumn col in dataTable.Columns) { sqlBulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName); } sqlBulkCopy.WriteToServer(dataTable); } transaction.Commit(); } catch (Exception) { transaction.Rollback(); throw; // 抛出异常让上层捕获处理 } } } } } }
额外注意事项
- 确保数据库目标表的列名、数据类型和CSV字段完全匹配,否则会出现列映射错误。
- 如果CSV里有大量空值或者特殊格式数据,建议在填充DataRow时做一下格式校验,避免导入失败。
- 对于8个表的场景,可以把每个表的初始化、填充逻辑都按这个模式扩展,保持代码的整洁性。
内容的提问来源于stack exchange,提问作者Foxem
相关产品推荐
相关产品推荐

