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

寻求ADO.NET批量插入方案——逐行插入速度极慢

ADO.NET 批量插入20K条记录的优化方案

针对20K条记录逐行插入耗时1小时的问题,以下是几种高效的ADO.NET批量插入实现方法:

1. 使用SqlBulkCopy(推荐,性能最优)

SqlBulkCopy是ADO.NET专门为SQL Server设计的批量数据写入组件,它能大幅减少网络往返次数,将数据批量写入数据库,是处理大量插入场景的首选方案。

实现代码

private async Task BulkInsertReprocessTsk(List<ReprocessTskChngs> reprocessTskChngs)
{
    using (SqlConnection conn = new SqlConnection(_connectionString))
    {
        await conn.OpenAsync();
        
        // 将数据转换为DataTable适配SqlBulkCopy
        DataTable dt = new DataTable();
        dt.Columns.Add("CRS", typeof(short));
        dt.Columns.Add("TskLOC", typeof(string));
        dt.Columns.Add("PROCESSED", typeof(int));
        
        foreach (var item in reprocessTskChngs)
        {
            dt.Rows.Add(item.CRS, item.TskLOC, item.PROCESSED);
        }
        
        using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, null))
        {
            // 映射DataTable列与数据库表列
            bulkCopy.DestinationTableName = "REPROCESS_TSK";
            bulkCopy.ColumnMappings.Add("CRS", "CRS");
            bulkCopy.ColumnMappings.Add("TskLOC", "TskLOC");
            bulkCopy.ColumnMappings.Add("PROCESSED", "PROCESSED");
            
            // 若DATE_ENTERED字段已设置默认值GETDATE(),无需额外处理,数据库会自动填充
            // 若未设置默认值,可在DataTable中添加该列并填充DateTime.Now
            
            // 可设置批次大小控制内存占用,例如:bulkCopy.BatchSize = 1000;
            await bulkCopy.WriteToServerAsync(dt);
        }
    }
}

2. 使用表值参数(Table-Valued Parameters, TVP)

通过自定义SQL Server表类型,将数据作为表参数传递,既能保持批量插入的高效性,又支持在插入时执行自定义业务逻辑,灵活性更强。

步骤1:创建SQL Server自定义表类型

CREATE TYPE dbo.ReprocessTskType AS TABLE
(
    CRS SMALLINT,
    TskLOC VARCHAR(MAX),
    PROCESSED INT
)

步骤2:ADO.NET实现代码

private async Task InsertWithTVP(List<ReprocessTskChngs> reprocessTskChngs)
{
    using (SqlConnection conn = new SqlConnection(_connectionString))
    {
        await conn.OpenAsync();
        
        string insertSql = @"
            INSERT INTO REPROCESS_TSK(CRS, TskLOC, PROCESSED, DATE_ENTERED)
            SELECT CRS, TskLOC, PROCESSED, GETDATE()
            FROM @ReprocessTskTable";
        
        using (SqlCommand cmd = new SqlCommand(insertSql, conn))
        {
            // 转换数据为DataTable
            DataTable dt = new DataTable();
            dt.Columns.Add("CRS", typeof(short));
            dt.Columns.Add("TskLOC", typeof(string));
            dt.Columns.Add("PROCESSED", typeof(int));
            
            foreach (var item in reprocessTskChngs)
            {
                dt.Rows.Add(item.CRS, item.TskLOC, item.PROCESSED);
            }
            
            // 添加表值参数
            SqlParameter tvpParam = cmd.Parameters.AddWithValue("@ReprocessTskTable", dt);
            tvpParam.SqlDbType = SqlDbType.Structured;
            tvpParam.TypeName = "dbo.ReprocessTskType";
            
            await cmd.ExecuteNonQueryAsync();
        }
    }
}

3. 批量提交参数化命令(兼容受限场景)

若无法使用前两种方案,可优化原有逐行插入逻辑,减少SqlCommand创建次数,按批次批量提交参数化语句。

优化代码

private async Task BatchInsertReprocessTsk(List<ReprocessTskChngs> reprocessTskChngs, int batchSize = 700)
{
    using (SqlConnection conn = new SqlConnection(_connectionString))
    {
        await conn.OpenAsync();
        
        string baseInsertSql = @"
            INSERT INTO REPROCESS_TSK(CRS, TskLOC, PROCESSED, DATE_ENTERED)
            VALUES ";
        
        // 按批次拆分数据,避免参数数量超过SQL Server默认限制(2100个)
        for (int i = 0; i < reprocessTskChngs.Count; i += batchSize)
        {
            var batch = reprocessTskChngs.Skip(i).Take(batchSize).ToList();
            using (SqlTransaction trans = conn.BeginTransaction())
            {
                try
                {
                    using (SqlCommand cmd = new SqlCommand("", conn, trans))
                    {
                        List<string> valueClauses = new List<string>();
                        for (int j = 0; j < batch.Count; j++)
                        {
                            string paramPrefix = $"@batch{j}_";
                            valueClauses.Add($"({paramPrefix}CRS, {paramPrefix}TskLOC, {paramPrefix}PROCESSED, GETDATE())");
                            
                            cmd.Parameters.Add($"{paramPrefix}CRS", SqlDbType.SmallInt).Value = batch[j].CRS;
                            cmd.Parameters.Add($"{paramPrefix}TskLOC", SqlDbType.VarChar).Value = batch[j].TskLOC;
                            cmd.Parameters.Add($"{paramPrefix}PROCESSED", SqlDbType.Int).Value = batch[j].PROCESSED;
                        }
                        
                        cmd.CommandText = baseInsertSql + string.Join(", ", valueClauses);
                        await cmd.ExecuteNonQueryAsync();
                    }
                    
                    await trans.CommitAsync();
                }
                catch
                {
                    trans.Rollback();
                    throw;
                }
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:30:45