C#批量插入数据到SqlServer中的四种方式

C#批量插入数据到SqlServer中的四种方式

2023年7月13日发(作者:)

C#批量插⼊数据到SqlServer中的四种⽅式 本篇,我将来讲解⼀下在Sqlserver中批量插⼊数据。 先创建⼀个⽤来测试的数据库和表,为了让插⼊数据更快,表中主键采⽤的是GUID,表中没有创建任何索引。GUID必然是⽐⾃增长要快的,因为你⽣成⼀个GUID算法所花的时间肯定⽐你从数据表中重新查询上⼀条记录的ID的值然后再进⾏加1运算要少。⽽如果存在索引的情况下,每次插⼊记录都会进⾏索引重建,这是⾮常耗性能的。如果表中⽆可避免的存在索引,我们可以通过先删除索引,然后批量插⼊,最后再重建索引的⽅式来提⾼效率。CREATE TABLE Product( Id UNIQUEIDENTIFIER PRIMARY KEY, NAME VARCHAR(50) NOT NULL, Price DECIMAL(18,2) NOT NULL)我们通过SQL脚本来插⼊数据,常见如下四种⽅式。⽅式⼀:⼀条⼀条插⼊,性能最差,不建议使⽤。INSERT INTO Product(Id,Name,Price) VALUES(newid(),'⽜栏1段',160);INSERT INTO Product(Id,Name,Price) VALUES(newid(),'⽜栏2段',260);......⽅式⼆:insert bulkBULK INSERT [ [ 'database_name'.][ 'owner' ].]{ 'table_name' FROM 'data_file' }

WITH (

[ BATCHSIZE [ = batch_size ] ],

[ CHECK_CONSTRAINTS ],

[ CODEPAGE [ = 'ACP' | 'OEM' | 'RAW' | 'code_page' ] ],

[ DATAFILETYPE [ = 'char' | 'native'| 'widechar' | 'widenative' ] ],

[ FIELDTERMINATOR [ = 'field_terminator' ] ],

[ FIRSTROW [ = first_row ] ],

[ FIRE_TRIGGERS ],

[ FORMATFILE = 'format_file_path' ],

[ KEEPIDENTITY ],

[ KEEPNULLS ],

[ KILOBYTES_PER_BATCH [ = kilobytes_per_batch ] ],

[ LASTROW [ = last_row ] ],

[ MAXERRORS [ = max_errors ] ],

[ ORDER ( { column [ ASC | DESC ] } [ ,...n ] ) ],

[ ROWS_PER_BATCH [ = rows_per_batch ] ],

[ ROWTERMINATOR [ = 'row_terminator' ] ],

[ TABLOCK ],

) 相关参数说明:BULK INSERT

[ database_name . [ schema_name ] . | schema_name . ] [ table_name | view_name ]

FROM 'data_file'

[ WITH

(

[ [ , ] BATCHSIZE = batch_size ] --BATCHSIZE指令来设置在单个事务中可以插⼊到表中的记录的数量

[ [ , ] CHECK_CONSTRAINTS ] --指定在⼤容量导⼊操作期间,必须检查所有对⽬标表或视图的约束。若没有 CHECK_CONSTRAINTS

选项,则所有 CHECK

和 FOREIGN KEY

约束都将被忽略,并且在此操作之后表的约束将标记为不可信。

[ [ , ] CODEPAGE = { 'ACP' | 'OEM' | 'RAW' | 'code_page' } ] --指定该数据⽂件中数据的代码页

[ [ , ] DATAFILETYPE =

{ 'char' | 'native'| 'widechar' | 'widenative' } ] --指定 BULK INSERT

使⽤指定的数据⽂件类型值执⾏导⼊操作。

[ [ , ] FIELDTERMINATOR = 'field_terminator' ] --标识分隔内容的符号

[ [ , ] FIRSTROW = first_row ] --指定要加载的第⼀⾏的⾏号。默认值是指定数据⽂件中的第⼀⾏

[ [ , ] FIRE_TRIGGERS ] --是否启动触发器

[ [ , ] FORMATFILE = 'format_file_path' ]

[ [ , ] KEEPIDENTITY ] --指定导⼊数据⽂件中的标识值⽤于标识列

[ [ , ] KEEPNULLS ] --指定在⼤容量导⼊操作期间空列应保留⼀个空值,⽽不插⼊⽤于列的任何默认值

[ [ , ] KILOBYTES_PER_BATCH = kilobytes_per_batch ]

[ [ , ] LASTROW = last_row ] --指定要加载的最后⼀⾏的⾏号

[ [ , ] MAXERRORS = max_errors ] --指定允许在数据中出现的最多语法错误数,超过该数量后将取消⼤容量导⼊操作。

[ [ , ] ORDER ( { column [ ASC | DESC ] } [ ,...n ] ) ] --指定数据⽂件中的数据如何排序

[ [ , ] ROWS_PER_BATCH = rows_per_batch ]

[ [ , ] ROWTERMINATOR = 'row_terminator' ] --标识分隔⾏的符号

[ [ , ] TABLOCK ] --指定为⼤容量导⼊操作持续时间获取⼀个表级锁

[ [ , ] ERRORFILE = 'file_name' ] --指定⽤于收集格式有误且不能转换为 OLE DB

⾏集的⾏的⽂件。

)]⽅式三:INSERT INTO xx select…INSERT INTO Product(Id,Name,Price) SELECT NEWID(),'⽜栏1段',160

UNION ALL

SELECT NEWID(),'⽜栏2段',180 ⽅式四:拼接SQLINSERT INTO Product(Id,Name,Price) VALUES(newid(),'⽜栏1段',160),(newid(),'⽜栏2段',260)......在C#中通过来实现批量操作存在四种与之对应的⽅式。⽅式⼀:逐条插⼊#region ⽅式⼀static void InsertOne(){ ine("采⽤⼀条⼀条插⼊的⽅式实现"); Stopwatch sw = new Stopwatch(); using (SqlConnection conn = new SqlConnection(StrConnMsg)) //using中会⾃动Open和Close

连接。 { string sql = "INSERT INTO Product(Id,Name,Price) VALUES(newid(),@p,@d)"; (); for (int i = 0; i < totalRow; i++) { using (SqlCommand cmd = new SqlCommand(sql, conn)) { hValue("@p", "商品" + i); hValue("@d", i); (); eNonQuery(); ine(("插⼊⼀条记录,已耗时{0}毫秒", dMilliseconds)); } if (i == getRow) { (); break; } } } ine(("插⼊{0}条记录,每{4}条的插⼊时间是{1}毫秒,预估总得插⼊时间是{2}毫秒,{3}分钟", totalRow, dMilliseconds,

((dMilliseconds / getRow) * totalRow), GetMinute((dMilliseconds / getRow * totalRow)), getRow));}static int GetMinute(long l){ return (Int32)l / 60000;}

#endregion 运⾏结果如下: 我们会发现插⼊100w条记录,预计需要50分钟时间,每插⼊⼀条记录⼤概需要3毫秒左右。⽅式⼆:使⽤SqlBulk#region ⽅式⼆static void InsertTwo(){ ine("使⽤Bulk插⼊的实现⽅式"); Stopwatch sw = new Stopwatch(); DataTable dt = GetTableSchema();

using (SqlConnection conn = new SqlConnection(StrConnMsg)) { SqlBulkCopy bulkCopy = new SqlBulkCopy(conn); ationTableName = "Product"; ize = ; (); (); for (int i = 0; i < totalRow;i++ ) { DataRow dr = (); dr[0] = d(); dr[1] = ("商品", i); dr[2] = (decimal)i; (dr); } if (dt != null && != 0) { oServer(dt); (); } ine(("插⼊{0}条记录共花费{1}毫秒,{2}分钟", totalRow, dMilliseconds, GetMinute(dMilliseconds))); }}static DataTable GetTableSchema(){ DataTable dt = new DataTable(); ge(new DataColumn[] {

new DataColumn("Id",typeof(Guid)),

new DataColumn("Name",typeof(string)),

new DataColumn("Price",typeof(decimal))}); return dt;}#endregion 运⾏结果如下: 插⼊100w条记录才8s多,是不是很溜。 打开Sqlserver Profiler跟踪,会发现执⾏的是如下语句:insert bulk Product ([Id] UniqueIdentifier, [NAME] VarChar(50) COLLATE Chinese_PRC_CI_AS, [Price] Decimal(18,2))⽅式三:使⽤TVPs(表值参数)插⼊数据 从sqlserver 2008起开始⽀持TVPs。创建缓存表ProductTemp ,执⾏如下SQL。CREATE TYPE ProductTemp AS TABLE( Id UNIQUEIDENTIFIER PRIMARY KEY, NAME VARCHAR(50) NOT NULL, Price DECIMAL(18,2) NOT NULL) 执⾏完成之后,会发现在数据库CarSYS下⾯多了⼀张缓存表ProductTemp 可见插⼊100w条记录共花费了11秒多。⽅式四:拼接SQL 此种⽅法在C#中有限制,⼀次性只能批量插⼊1000条,所以就得分段进⾏插⼊。#region

⽅式四static void InsertFour(){ ine("采⽤拼接批量SQL插⼊的⽅式实现"); Stopwatch sw = new Stopwatch(); using (SqlConnection conn = new SqlConnection(StrConnMsg)) //using中会⾃动Open和Close

连接。 { (); (); for (int j = 0; j < totalRow / getRow;j++ ) { StringBuilder sb = new StringBuilder(); ("INSERT INTO Product(Id,Name,Price) VALUES"); using (SqlCommand cmd = new SqlCommand()) {

for (int i = 0; i < getRow; i++) { Format("(newid(),'商品{0}',{0}),", j*i+i); } tion = conn; dText = ng().TrimEnd(','); eNonQuery(); } } (); ine(("插⼊{0}条记录,共耗时{1}毫秒",totalRow,dMilliseconds)); }}#endregion 运⾏结果如下: 我们可以看到⼤概花费了10分钟。虽然在⽅式⼀的基础上,性能有了较⼤的提升,但是显然还是不够快。总结: ⼤数据批量插⼊⽅式⼀和⽅式四尽量避免使⽤,⽽⽅式⼆和⽅式三都是⾮常⾼效的批量插⼊数据⽅式。其都是通过构建DataTable的⽅式插⼊的,⽽我们知道DataTable是存在内存中的,所以当数据量特别特别⼤,⼤到内存中⽆法⼀次性存储的时候,可以分段插⼊。⽐如需要插⼊9千万条数据,可以分成9段进⾏插⼊,⼀次插⼊1千万条。⽽在for循环中直接进⾏数据库操作,我们是应该尽量避免的。每⼀次数据库的连接、打开和关闭都是⽐较耗时的,虽然在C#中存在数据库连接池,也就是当我们使⽤using或者(),进⾏释放连接时,其实并没有真正关闭数据库连接,它只是让连接以类似于休眠的⽅式存在,当再次操作的时候,会从连接池中找⼀个休眠状态的连接,唤醒它,这样可以有效的提⾼并发能⼒,减少连接损耗。⽽连接池中的连接数,我们都是可以配置的。

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1689246325a225612.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信