You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:06:40