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

C#实现3GB级大CSV文件高效导入SQL表的方案咨询(需为每行附加文件名与日期字段)

高效导入大CSV到SQL:CsvHelper + SqlBulkCopy 解决方案

嘿,你选CsvHelper搭配SqlBulkCopy绝对是找对方向了——这俩组合处理大文件的效率比EF循环高太多,完美解决你3GB文件导入慢的问题!针对你需要给每行附加文件名和日期的需求,咱们可以通过在读取CSV时动态注入字段的方式实现,下面给你详细的步骤和代码:

第一步:安装必要的NuGet包

首先确保你安装了这两个包:

  • CsvHelper:用来高效读取CSV/TSV文件
  • Microsoft.Data.SqlClient(或System.Data.SqlClient,根据你的.NET版本选择):提供SqlBulkCopy功能

第二步:定义实体类与Csv映射

先把你的目标实体类整理好,包含CSV里的字段加上需要附加的IMPORT_FILE_NM和IMPORT_DT:

public class ImportDestinationTable
{
    public string ImportCol0 { get; set; }
    public string ImportCol1 { get; set; }
    public int? ImportCol2 { get; set; }
    public string ImportCol3 { get; set; }
    public string ImportCol4 { get; set; }
    public string ImportCol5 { get; set; }
    public string IMPORT_FILE_NM { get; set; }
    public DateTime IMPORT_DT { get; set; }
}

然后定义CsvHelper的映射类,用来处理CSV列和实体属性的对应,同时处理Nullable类型的转换:

using CsvHelper.Configuration;

public class ImportDestinationTableMap : ClassMap<ImportDestinationTable>
{
    public ImportDestinationTableMap()
    {
        // 按CSV列索引映射(对应你原来的row[0]、row[1]...)
        Map(m => m.ImportCol0).Index(0);
        Map(m => m.ImportCol1).Index(1);
        // 把空字符串转换成null,替代你原来的TryParseNullable方法
        Map(m => m.ImportCol2).Index(2).TypeConverterOption.NullValues("");
        Map(m => m.ImportCol3).Index(3);
        Map(m => m.ImportCol4).Index(4);
        Map(m => m.ImportCol5).Index(5);
        
        // 这两个字段CSV里没有,告诉CsvHelper忽略它们,后面手动赋值
        Map(m => m.IMPORT_FILE_NM).Ignore();
        Map(m => m.IMPORT_DT).Ignore();
    }
}

第三步:实现高效批量导入的核心代码

下面是替换你原有Process方法的代码,重点是流式读取+批量导入,不会把整个大文件加载到内存:

using CsvHelper;
using CsvHelper.Configuration;
using Microsoft.Data.SqlClient;
using System.Globalization;
using System.Reflection;

public void Process(string inputFilePath)
{
    DateTime fileDate = DateTime.Today;
    string[] files = Directory.GetFiles(inputFilePath);
    // 替换成你的数据库连接字符串
    string connectionString = "Server=your-server;Database=your-db;Trusted_Connection=True;";

    foreach (var file in files)
    {
        string fileName = Path.GetFileName(file);
        var csvConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            Delimiter = "\t", // 你的文件是制表符分隔的,对应原来的Split('\t')
            HasHeaderRecord = true,
            // 跳过前两行(对应你原来代码里的两次reader.ReadLine())
            ShouldSkipRecord = record => record.Context.Row <= 2,
            // 如果不需要严格验证表头和缺失字段,可以关闭这些检查提升速度
            HeaderValidated = null,
            MissingFieldFound = null
        };

        using (var streamReader = new StreamReader(file))
        using (var csvReader = new CsvReader(streamReader, csvConfig))
        {
            // 注册映射类
            csvReader.Context.RegisterClassMap<ImportDestinationTableMap>();

            // 流式读取CSV记录,并注入文件名和日期——这里不会一次性加载所有数据到内存
            var records = csvReader.GetRecords<ImportDestinationTable>()
                .Select(record =>
                {
                    record.IMPORT_FILE_NM = fileName;
                    record.IMPORT_DT = fileDate;
                    return record;
                });

            // 使用SqlBulkCopy批量写入数据库
            using (var bulkCopy = new SqlBulkCopy(connectionString))
            {
                bulkCopy.DestinationTableName = "ImportDestinationTable"; // 你的SQL表名
                bulkCopy.BatchSize = 10000; // 每次批量写入的行数,可根据内存调整(比如1万-10万)
                bulkCopy.BulkCopyTimeout = 300; // 超时时间(秒),避免大批次超时
                bulkCopy.EnableStreaming = true; // 开启流式传输,进一步降低内存占用

                // 自动映射实体属性和数据库列名(如果两者名称一致的话)
                foreach (PropertyInfo prop in typeof(ImportDestinationTable).GetProperties())
                {
                    bulkCopy.ColumnMappings.Add(prop.Name, prop.Name);
                }

                // 执行批量写入
                bulkCopy.WriteToServer(records.AsDataReader());
            }

            Console.WriteLine($"✅ 文件 {fileName} 导入完成!");
        }
    }
}

关键优化点说明

  1. 流式处理:csvReader.GetRecords()返回的是可枚举对象,配合Select注入字段时,不会一次性把3GB文件加载到内存,而是边读边处理,避免内存溢出。
  2. SqlBulkCopy效率:相比EF的逐行Add+SaveChanges,SqlBulkCopy直接和数据库底层交互,用批量插入的方式,速度能提升几十甚至上百倍。
  3. 跳过前两行:通过ShouldSkipRecord配置替代手动ReadLine(),代码更简洁且符合CsvHelper的使用规范。
  4. Nullable转换:映射类里的TypeConverterOption.NullValues("")自动处理空字符串转null,替代你原来的TryParseNullable方法。

额外优化建议

  • 临时禁用索引:导入前禁用目标表的非聚集索引,导入完成后再重建,能大幅降低写入开销(索引会减慢插入速度)。
  • 调整BatchSize:如果你的服务器内存充足,可以把BatchSize调大(比如5万或10万),减少数据库交互次数;内存紧张则调小。
  • 错误处理:可以添加try-catch块捕获SqlException,处理导入过程中的异常(比如数据格式错误)。

内容的提问来源于stack exchange,提问作者caj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 14:27:28