注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

樱之花

叶散的时候,你明白欢聚;花谢的时候,你明白青春.

 
 
 

日志

 
 
关于我

分类中“我的实验室”是我在日常工作中的一些知识总结,有些写的比较匆忙,可能大家在阅读时会产生困扰,后期有时间我会重新整理编辑,谢谢大家的到访,您们的支持是我前进的动力!

网易考拉推荐

SqlBulkCopy 海量数据填充数据库  

2011-11-23 15:01:47|  分类: SQLServer |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

SqlBulkCopy类,它可以很快速的批量写数据库

最近在将excel中的文件导入到数据库中,用程序进行编写,由于数据量较大所以速度很慢,后来采用了SqlBulkCopy类,解决了速度的问题,我就insert语句,sqldataadapter.update(dataset,tablename);sqlbulkcopy.WriteToServer(datatable);三个方法的性能进行比较:

 

1、生成测试的datatable表,表结构如下:

UniqueID(主键,自动增长)  |  CompanyName  |  CompanyCode  |  Address  |  Owner  |  Memo

共6个字段。

 SqlConnection sqlconnection = new SqlConnection(connectionString);
 SqlDataAdapter sqldataadapter = new SqlDataAdapter("select * from Table_1 where 1=2", sqlconnection);
 DataSet dataset = new DataSet();
 sqldataadapter.Fill(dataset, "Table_1");
 DataTable datatable = dataset.Tables[0];
  //生成20000条记录

  for (int i = 0; i < 20000; i++)
  {
          DataRow datarow = datatable.NewRow();
          datarow["CompanyName"] = "companyname"+string.Format("{0:0000}",i);
          datarow["CompanyCode"] = "companycode" + string.Format("{0:0000}", i);
          datarow["Address"] = "address" + string.Format("{0:0000}", i);
          datarow["Owner"] = "owner" + string.Format("{0:0000}", i);
          datarow["Memo"] = "memo" + string.Format("{0:0000}", i);

          datatable.Rows.Add(datarow);
  }

 

2、使用sqlcommand.executenonquery()方法插入

foreach (DataRow datarow in datatable.Rows)
{
string sql = "INSERT INTO [Table_1]

([CompanyName],[CompanyCode],[Address],[Owner],[Memo])" +
"VALUES('" + datarow["CompanyName"].ToString() + "'" +
",'" + datarow["CompanyCode"].ToString() + "'" +
",'" + datarow["Address"].ToString() + "'" +
",'" + datarow["Owner"].ToString() + "'" +
",'" + datarow["Memo"].ToString() + "')";
  using (SqlConnection sqlconn = new SqlConnection(connectionString))
  {
      sqlconn.Open();

      SqlCommand sqlcommand = new SqlCommand(sql, sqlconn);
      sqlcommand.ExecuteNonQuery();
      sqlconn.Close();
  }
}

插入20000条记录时间:00:00:29.7336000

 

3、使用sqldataadapter.update(dataset,tablename);

SqlCommand insertcommand = new SqlCommand("INSERT INTO [Table_1]([CompanyName],[CompanyCode],[Address],[Owner],[Memo])" +
 "VALUES(@CompanyName, @CompanyCode,@Address,@Owner,@Memo)",new SqlConnection(connectionString));
 insertcommand.Parameters.Add("@CompanyName", SqlDbType.NChar, 50, "CompanyName");
 insertcommand.Parameters.Add("@CompanyCode", SqlDbType.NChar, 25, "CompanyCode");
insertcommand.Parameters.Add("@Address", SqlDbType.NChar, 255, "Address");
insertcommand.Parameters.Add("@Owner", SqlDbType.NChar, 25, "Owner");
insertcommand.Parameters.Add("@Memo", SqlDbType.NChar, 255, "Memo");
sqldataadapter.InsertCommand = insertcommand;

sqldataadapter.Update(dataset, "Table_1");  

插入20000条记录时间:00:00:22.8938000

 

4、使用sqlbulkcopy.writetoserver(datatable)

SqlBulkCopy sqlbulkcopy = new SqlBulkCopy(connectionString, SqlBulkCopyOptions.UseInternalTransaction);
sqlbulkcopy.DestinationTableName = "Table_1";//数据库中的表名

sqlbulkcopy.WriteToServer(dataset.Tables[0]);

插入20000条记录时间:00:00:00.3276000

所以说速度是sqlbulkcopy最快,sqldataadapter.update()次之,sqlcommand.ExecuteNonQuery()最慢。

测试结果

 

 

我有个习惯,就是新的事物总抱有怀疑的态度,于是发动几个同事一起测试,结果确实很让我吃惊。平均导入速度要比之前传统的方式快10倍左右。

 

查了些资料,微软是这样定义的:

SqlBulkCopy 类允许编写提供类似功能的托管代码解决方案。还有其他将数据加载到 SQL Server 表的方法(例如 INSERT 语句),但相比之下 SqlBulkCopy 提供明显的性能优势。
使用 SqlBulkCopy 类只能向 SQL Server 表写入数据。但是,数据源不限于 SQL Server;可以使用任何数据源,只要数据可加载到 DataTable 实例或可使用 IDataReader 实例读取数据。

补充:SqlBulkCopy是.NET Framework 2.0新增的类,位于命名空间System.Data.SqlClient下,主要提供把其他数据源的数据有效批量的加载到SQL Server表中的功能。类似与 Microsoft SQL Server 包中名为 bcp 的命令行应用程序。但是使用 SqlBulkCopy 类可以编写托管代码解决方案,性能上优于bcp命令行应用程序,更优于如Insert方式向SQL Server表加载大量数据。SqlBulkCopy可以应用到大批量数据的转移上,而不管数据源是什么。

 

另外,《使用asp.net 2.0中的SqlBulkCopy类批量复制数据》说:

­­SqlBulkCopy 包含一个方法 WriteToServer,它用来从数据的源复制数据到数据的目的地。 WriteToServer方法可以处理的数据类型有DataRow[]数组,DataTable 和 DataReader。 你可以根据不同的情形使用不同的数据类型,但是更多时候选择DataReader是一个比较好的主意。 这是因为DataReader是一个只向前的、只读的数据流,它不会保存数据,所以要比DataTable 和 DataRows[]都要快。

 

下面的代码的作用是把数据从源表复制到目的表。
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection.ConnectionString))
      {
        bulkCopy.BatchSize = 500;

        bulkCopy.NotifyAfter = 1000;
        bulkCopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(bulkCopy_SqlRowsCopied);
        bulkCopy.DestinationTableName = "Products_Latest";
        bulkCopy.WriteToServer(reader);                   
      }

这里有一对需要提及的知识点。 首先,我使用DataReader来从数据库的表中读取数据。 Products_Latest是目的表,因为数据要从Products_Archive表复制到Products_Latest表。 bulkCopy对象提供了一个SqlRowCopied事件,在每次处理完NotifyAfter属性指定的行数时发生。 本例中的意思就是每处理完1000行就触发一次该事件,因为NotifyAfter被设置成了1000

BatchSize属性是非常重要的,程序性能如何主要就依靠着它。 BatchSize的意思就是同每一批次中的行数,在每一批次结束时,就将该批次中的行发送到数据库。 我将BatchSize设置成了500,其意思就是reader每读出500行就将他们发送到数据库从而执行批量复制的操作。 BatchSize的默认值是“1”,其意思就是把每一行作为一个批次发送到数据库。

设置不同的BatchSize在性能上将给你带来不同的结果。 你应该根据你的需求进行测试,来决定BatchSize的大小。

 

查了下原理:

SqlBulkCopy让 DataSet 内大量的数据或是 DataReader通过数据流(Stream)直接读取大量的记录,可以快速将这些记录添加到目的数据库的数据表中。但要注意的是它并非如我们一般用的 bcp.exe工具程序,可以从某个符号分隔文件读取大量数据,选择性地搭配格式文件(Format File)将记录装载到数据库中,或是将数据库内的数据导出成为一个文件。但由于DataSet 能集成 XML 数据,因此依然可以采用 SqlBulkCopy 类型,轻松地通过 DataSet 将 XML 文件数据大量转入到数据库。

总结

 

在了解了SqlBulkCopy之后,我们基本上所有大数据都是用它实现的,有人说SqlBulkCopy不是走ADO.NET的而是直接操作MDB文件,我也没有证实。但我想暂时不影响我实用它。当然,SqlBulkCopy使用时要注意以下几点:

1.确认确实需要大容量更新在执行此操作,(几十行的数据请尽量使用别的渠道把).

2.确认数据一致性,与检查机制,以免遇到主键冲突,数据不符格式等意外。

3.SqlBulkCopy操作可能会导致对目标表元数据的更改(例如,禁用约束检查时)。如果出现这种情况,访问大容量插入表的并发快照隔离事务将失败。

4. SqlBulkCopy将向数据库下大容量更新锁,请注意并发性,以免其他连接因长时间等待而超时。

 

延伸

SqlBulkCopy的快速数据复制,不得不让我想到数据库备份及数据库读写分离的多机数据同步。刚才在找资料时,也看到了一篇SqlBulkcopy的应用《利用SqlBulkCopy实现网上答题系统》。当然,数据库读写分离后数据同步是一门很大的学问。

  评论这张
 
阅读(2178)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017