首页 游戏 软件 资讯 排行榜 专题
首页
编程语言
c#如何批量插入数据_c#批量插入数据完整教程与实战案例

c#如何批量插入数据_c#批量插入数据完整教程与实战案例

热心网友
52
转载
2026-05-06

C# 万级数据批量插入:SqlBulkCopy 实战精要

c#如何批量插入数据_c#批量插入数据完整教程与实战案例

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

在C#中进行大规模数据插入,性能是首要考量。当数据量达到万级甚至更高时,常规的逐条插入方法会迅速成为性能瓶颈。那么,有没有一种既高效又稳定的解决方案呢?答案是肯定的。

用 SqlBulkCopy 实现高速批量插入

开门见山地说,在C#生态中,处理万级以上数据批量插入任务,SqlBulkCopy几乎是唯一正确的选择。它的工作原理是绕过了SQL语句的解析层,直接利用SQL Server的原生批量加载协议。这种“走后门”的方式,带来的性能提升是数量级的——通常能达到普通INSERT INTO ... VALUES循环操作的10到50倍。

一个常见的性能误区是试图用ExecuteNonQuery配合参数化INSERT循环来模拟批量操作。结果往往是,插入一万条数据可能需要耗时30秒以上。而同样的数据量,交给SqlBulkCopy处理,通常在300毫秒内就能完成。这个差距,足以决定一个功能的体验是“流畅”还是“卡顿”。

在实际使用时,有几个关键点需要把握:

  • 适用范围SqlBulkCopy是SQL Server(包括Azure SQL)的“亲儿子”,专为其优化。它不适用于MySQL、PostgreSQL等其他数据库。
  • 前置条件:目标表必须已经存在。SqlBulkCopy只管插入,不会自动建表,也不会对字段映射做智能校验——映射错了,它要么静默跳过,要么直接报错。
  • 数据源选择:推荐使用DataTableIDataReader作为数据源。尽量避免直接传递IEnumerable,因为这会触发完整的枚举和反射过程,反而会拖累性能。
  • 核心配置BatchSize(批次大小,建议在1000到10000之间)、DestinationTableName(目标表名,务必使用全名如[dbo].[Orders])、EnableStreaming = true(处理海量数据时启用,能有效减少内存占用)。

DataTable 构造时字段顺序与类型必须严格匹配目标表

这是新手最容易栽跟头的地方:代码运行不报错,但数据就是插不进去;或者数值被截断了,日期变成了1900-01-01。问题的根源,十有八九是DataTable的列顺序或数据类型与数据库表结构没有严格对齐。

举个例子,假设目标表是这样定义的:

CREATE TABLE Orders (
    Id INT IDENTITY(1,1) PRIMARY KEY,
    OrderNo NVARCHAR(20) NOT NULL,
    Amount DECIMAL(18,2),
    CreatedAt DATETIME2
)

那么,对应的DataTable就必须严格按照这个顺序来添加列,并且数据类型要兼容:

var dt = new DataTable();
dt.Columns.Add("OrderNo", typeof(string));      // 注意:第一列不是“Id”
dt.Columns.Add("Amount", typeof(decimal));
dt.Columns.Add("CreatedAt", typeof(DateTime));  // 这里不能用 DateTime? 或 string

还有一个细节:如果数据库表中的某个字段允许为NULL,那么DataTable中对应列的AllowDBNull属性也必须设为true。否则,SqlBulkCopy会抛出InvalidOperationException异常。

处理主键冲突:用临时表 + MERGE 替代直接插入

SqlBulkCopy本身的设计哲学是“高速写入”,因此它不支持类似ON CONFLICTIGNORE这样的冲突处理逻辑。一旦遇到主键或唯一键重复,它会直接报错并中断整个操作。但在真实的业务场景中,“存在则更新,不存在则插入”的需求非常普遍。

这时,一个经典的解决方案是分两步走:

  • 第一步,暂存数据:先用SqlBulkCopy将数据高速导入一个结构相同的临时表(例如#staging_orders)。
  • 第二步,合并数据:再执行一条T-SQL的MERGE语句,将临时表中的数据智能地合并到正式表中。

下面是一个典型的MERGE语句示例:

MERGE Orders AS tgt
USING #staging_orders AS src
ON tgt.OrderNo = src.OrderNo
WHEN MATCHED THEN
    UPDATE SET Amount = src.Amount, CreatedAt = src.CreatedAt
WHEN NOT MATCHED THEN
    INSERT (OrderNo, Amount, CreatedAt) VALUES (src.OrderNo, src.Amount, src.CreatedAt);

需要注意的是,以#为前缀的临时表是会话级别的,会话结束后会自动清理,无需手动删除。如果需要在多个数据库连接间共享临时数据,可以使用##前缀的全局临时表,但必须特别注意并发安全问题。

异步与事务控制:BulkCopy 本身不支持 async,但可包裹在 TransactionScope 中

SqlBulkCopyWriteToServer方法是一个同步阻塞调用,即便在.NET 6及更高版本中,也没有提供原生的WriteToServerAsync方法。如果希望不阻塞主线程,通常的做法是用Task.Run将其包裹起来在后台线程执行,但要切记,数据库连接对象不能跨线程共享。

事务控制则是另一个更关键的话题。默认情况下,SqlBulkCopy的每个批次(Batch)都是一个独立的事务。这意味着如果中途失败,只有当前批次的数据会被回滚。如果需要保证整批操作的原子性(要么全部成功,要么全部回滚),就必须显式地传入一个已开启的SqlTransaction对象。

using var conn = new SqlConnection(connStr);
conn.Open();
using var tx = conn.BeginTransaction();
using var bulk = new SqlBulkCopy(conn) {
    DestinationTableName = "[dbo].[Orders]",
    SqlTransaction = tx  // ⚠️ 这是实现原子操作的关键
};
bulk.WriteToServer(dt);
tx.Commit(); // 全部成功后提交,否则调用 tx.Rollback()

还有一个容易被忽略的细节:默认情况下,SqlBulkCopy会禁用目标表上的触发器以追求极致速度。如果业务逻辑依赖这些触发器,需要显式设置FireTriggers = true,但这会带来明显的性能损耗。

来源:https://www.php.cn/faq/2321251.html
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

C#怎么使用ReadOnlySpan_C#只读内存切片性能优化教程【高级】
编程语言
C#怎么使用ReadOnlySpan_C#只读内存切片性能优化教程【高级】

C ReadOnlySpan 使用指南:高性能只读内存切片优化技巧【高级教程】 在 NET 高性能编程实践中,尤其是在字符串处理场景,一个公认的高效策略是:直接采用 ReadOnlySpan 来替代传统的 string 参数以及中间的 Substring 调用。这是目前实现零分配、低开销处理的最

热心网友
05.06
c#如何实现分页查询_c#分页查询最全用法总结
编程语言
c#如何实现分页查询_c#分页查询最全用法总结

SQL Server分页首选OFFSET-FETCH,需配合ORDER BY且参数化传值;EF Core用Skip Take自动翻译,避免内存分页;大数据量时应改用游标分页。 SQL Server 中用 OFFSET-FETCH 做分页最直接 说到在SQL Server里做分页,2012及以上版本提

热心网友
05.06
c#如何批量插入数据_c#批量插入数据完整教程与实战案例
编程语言
c#如何批量插入数据_c#批量插入数据完整教程与实战案例

C 万级数据批量插入:SqlBulkCopy 实战精要 在C 中进行大规模数据插入,性能是首要考量。当数据量达到万级甚至更高时,常规的逐条插入方法会迅速成为性能瓶颈。那么,有没有一种既高效又稳定的解决方案呢?答案是肯定的。 用 SqlBulkCopy 实现高速批量插入 开门见山地说,在C 生态中,

热心网友
05.06
c#如何使用TestContainers集成测试_c#TestContainers集成测试的最佳实践与常见坑点
编程语言
c#如何使用TestContainers集成测试_c#TestContainers集成测试的最佳实践与常见坑点

C 中使用TestContainers进行集成测试:最佳实践与常见坑点 想在 NET 里玩转 TestContainers?这事儿说简单也简单,说麻烦也麻烦。简单在于,它确实能让你用几行代码就拉起一个数据库或中间件进行测试;麻烦在于,从环境配置到代码编写,每一步都有几个“经典”的坑在等着你。今天,

热心网友
05.06
C#怎么操作WPF Canvas画布绘图 C#如何在WPF Canvas上用代码动态绘制图形和连线【控件】
编程语言
C#怎么操作WPF Canvas画布绘图 C#如何在WPF Canvas上用代码动态绘制图形和连线【控件】

C WPF Canvas画布绘图完全指南:代码动态绘制图形与连线详解 Canvas直接添加子元素导致错位或不显示的解决方案 许多C 开发者在初次使用WPF Canvas控件进行动态绘图时,常会遇到一个典型问题:为何通过代码添加的Rectangle矩形或Line线条无法正常显示,或者出现位置偏移?

热心网友
05.06

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

荣耀400pro关机要按几秒
电脑教程
荣耀400pro关机要按几秒

荣耀400 Pro正确关机全指南:从常规操作到故障应对详解 需要关闭您的荣耀400 Pro手机?日常操作其实非常简便。只需长按位于机身右侧的电源键约3秒钟,屏幕上便会浮现一个简洁的半透明菜单,其中明确列出了“关机”、“重启”以及“紧急呼叫”选项。直接点击“关机”,系统将启动一次10秒的安全倒计时,随

热心网友
05.06
红米K30Pro如何拆后盖胶怎么清理
电脑教程
红米K30Pro如何拆后盖胶怎么清理

红米K30 Pro后盖拆解教程:专业工具与细致手法的完美结合 红米K30 Pro的后盖采用了高强度背胶配合隐藏式螺丝的双重固定设计,想要实现无损拆解,绝非依靠蛮力可以完成。整个操作流程对加热温度、撬启手法以及清洁标准都有严格要求,任何环节的疏忽都可能导致部件损伤。具体而言,其后盖边缘使用了耐高温的工

热心网友
05.06
三星zflip电池百分比需要root吗
电脑教程
三星zflip电池百分比需要root吗

无需Root权限:三星Galaxy Z Flip系列电量数字显示设置全解析 很多三星折叠屏手机用户都想知道,如何在状态栏直接查看精确的电池百分比数字,是否必须获取Root权限才能实现?实际上完全不需要。三星自Galaxy Z Flip 5、Z Flip 4等主流机型开始,已在系统层面内置了这一实用功

热心网友
05.06
笔记本开机自检时能看到DDR3或DDR4吗
电脑教程
笔记本开机自检时能看到DDR3或DDR4吗

笔记本开机自检信息虽不直接标注“DDR3”或“DDR4”,但联想、戴尔、华硕等品牌BIOS画面常以“PC3-”或“PC4-”编码间接揭示内存代际。UEFI自检显示的内存频率(如2400MHz 3200MHz)结合JEDEC规范可辅助推断:PC3对应DDR3,PC4对应DDR4。更高精度的识别方案包括

热心网友
05.06
空调制冷但不太凉是压缩机问题吗?
电脑教程
空调制冷但不太凉是压缩机问题吗?

空调制冷不足怎么办?先别急着维修压缩机,这些问题更常见 夏天开空调却感觉不够凉爽?很多朋友的第一反应是压缩机坏了,其实压缩机故障的概率相对较低。根据维修行业的大数据统计,绝大多数制冷效果不佳的情况,源于几个容易被忽略的日常维护与环境因素。滤网积尘、制冷剂泄漏、外机散热不良才是真正的高发原因。盲目更换

热心网友
05.06