sqlbulkcopy是sql server专属高速批量插入方案,基于tds协议绕过sql解析,不支持mysql或postgresql;需确保连接已打开、表结构预先存在、目标表名用三段式格式,且不支持自动建表。

SqlBulkCopy 是 SQL Server 专属的“高速通道”,别拿它连 MySQL 或 PostgreSQL
SqlBulkCopy 只能用于 SQL Server,底层直接调用 TDS 协议绕过 SQL 解析,所以快。如果你在连接字符串里填的是 MySQL 的地址,SqlBulkCopy 会直接抛出 InvalidOperationException:“The given SqlConnection is not valid for SqlBulkCopy.”——不是配置错,是根本不能用。
常见错误现象:SqlBulkCopy 构造时传入已关闭的连接、或连接字符串没开 TrustServerCertificate=true(尤其在启用了加密的 Azure SQL 上),都会在 WriteToServer 调用时才报错,而不是构造时。
- 确保
SqlConnection处于Open状态,且未被其他SqlBulkCopy实例复用 - 目标表名必须是完整三段式(如
[dbo].[Users]),否则可能写入到tempdb或报错 - 不支持自动创建目标表;表结构必须提前存在,且列名/类型需与
DataTable或IDataReader匹配
Dapper 本身不提供原生批量插入,靠的是参数化拼接 + 手动分批
Dapper 没有 InsertAll 这类方法,所谓“Dapper 批量插入”本质是把多条 INSERT INTO ... VALUES (@p0, @p1), (@p2, @p3)... 拼成一条语句发送。它快,是因为减少了网络往返,但受 SQL Server 单语句参数上限(2100 个)和最大长度(~65KB)限制。
典型踩坑:用 connection.Execute("INSERT INTO t(x) VALUES (@x)", list) 直接传大集合,Dapper 会尝试展开全部参数,一旦超过 2100 个就崩,报错信息是 The incoming request has too many parameters. The server supports a maximum of 2100 parameters.
- 必须手动切片:每批 ≤ 900 行(留余量防字段多时超限)
- 推荐用
List<t>.AsEnumerable().Batch(900)</t>(需引用MoreLinq)或手写for循环分段 - 避免在循环内反复
new SqlCommand,复用同一个IDbConnection实例即可
SqlBulkCopy + Dapper 混合用法:临时表中转更新/UPSERT 场景
当你要做批量 UPDATE 或 MERGE(比如根据 ID 更新部分字段),SqlBulkCopy 无法直写目标表(无 WHERE 条件),但可以先写进临时表,再用 Dapper 执行 MERGE 或 UPDATE FROM。
关键点不在“怎么写”,而在“怎么不锁死”:如果目标表有高并发读,MERGE 默认加范围锁,容易阻塞。此时应在临时表建好后,用 WITH (TABLOCK) 提示减少锁粒度:
MERGE [dbo].[Users] AS target USING #TempStaging AS source ON target.Id = source.Id WHEN MATCHED THEN UPDATE SET Name = source.Name WHEN NOT MATCHED THEN INSERT (Id, Name) VALUES (source.Id, source.Name) OPTION (TABLOCK);
- 临时表名必须带
#前缀,且作用域限于当前连接 -
SqlBulkCopy.DestinationTableName设为"#TempStaging"即可,无需提前CREATE TABLE(Dapper 不管这个) - 整个流程必须在同一个
SqlConnection中完成,否则临时表不可见
性能差异真实可见:10 万条数据,三种写法耗时对比
实测环境:SQL Server 2022(本地实例),单机 SSD,10 万条含 5 字段的记录(平均行宽 ~200B)。
- 纯 Dapper 参数化拼接(每批 900 行):约 8.2 秒
- SqlBulkCopy(
BatchSize=5000):约 1.3 秒 - SqlBulkCopy + MERGE 临时表(含建表+清理):约 2.1 秒
差距主要来自:Dapper 仍要走 SQL 解析和计划缓存查找;SqlBulkCopy 绕过解析,直接灌数据页。但注意——SqlBulkCopy 对内存压力更敏感,DataTable 装 10 万行可能吃掉 150MB+ 托管堆,而 Dapper 分批时每批只占几 MB。











