SqlBulkCopy是.net2.0的新特性,平时用的很少,但是其功能却是非常强大,对于批量插入数据性能非常优越
private void BulkInsert()
{
SqlConnection sqlcon = new SqlConnection("Data Source=LocalHost;Integrated Security=SSPI;Initial Catalog=xiaotest;");
DateTime beginTime = DateTime.Now;
DataTable dt = new DataTable();
dt.Columns.Add("n", typeof(string));
dt.Columns.Add("name", typeof(string));
for (int i = 1; i < 1000; i++)
{
DataRow r = dt.NewRow();
r["n"] = i;
r["name"] = "xiao";
dt.Rows.Add(r);
}
sqlcon.Open();
using (SqlBulkCopy bulk = new SqlBulkCopy("Data Source=LocalHost;Integrated Security=SSPI;Initial Catalog=xiaotest;"))
{
bulk.BatchSize = 1000;
bulk.DestinationTableName = "test2";
bulk.ColumnMappings.Add("n", "n");
bulk.ColumnMappings.Add("name", "name");
bulk.WriteToServer(dt);
}
DateTime endTime = DateTime.Now;
TimeSpan useTime = endTime - beginTime;
dt.Dispose();
time = "使用时间" + useTime.TotalSeconds.ToString() + "秒";
sqlcon.Close();
sqlcon.Dispose();
}
经过1000条数据的对比测试,一般性的循环插入与sqlbulk插入的时间对比为:
一般插入:使用时间0.5200008秒
使用builk插入:使用时间0.02秒
性能非常优越
另一种写法:
首先说一下,IProvider里有一个用于实现批量插入的插件服务接口IBatcherProvider,此接口在前一篇文章中已经提到过了。
///
/// 提供数据批量处理的方法。
///
public interface IBatcherProvider : IProviderService
{
///
/// 将
///
/// 要批量插入的
/// 每批次写入的数据量。
void Insert(DataTable dataTable, int batchSize = 10000);
}
一、SqlServer数据批量插入
SqlServer的批量插入很简单,使用SqlBulkCopy就可以,以下是该类的实现:
///
/// 为 System.Data.SqlClient 提供的用于批量操作的方法。
///
public sealed class MsSqlBatcher : IBatcherProvider
{
///
/// 获取或设置提供者服务的上下文。
///
public ServiceContext ServiceContext { get; set; }
///
/// 将
///
/// 要批量插入的
/// 每批次写入的数据量。
public void Insert(DataTable dataTable, int batchSize = 10000)
{
Checker.ArgumentNull(dataTable, "dataTable");
if (dataTable.Rows.Count == 0)
{
return;
}
using (var connection = (SqlConnection)ServiceContext.Database.CreateConnection())
{
try
{
connection.TryOpen();
//给表名加上前后导符
var tableName = DbUtility.FormatByQuote(ServiceContext.Database.Provider.GetService
using (var bulk = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, null)
{
DestinationTableName = tableName,
BatchSize = batchSize
})
{
//循环所有列,为bulk添加映射
dataTable.EachColumn(c => bulk.ColumnMappings.Add(c.ColumnName, c.ColumnName), c => !c.AutoIncrement);
bulk.WriteToServer(dataTable);
bulk.Close();
}
}
catch (Exception exp)
{
throw new BatcherException(exp);
}
finally
{
connection.TryClose();
}
}
}
}
SqlBulkCopy的ColumnMappings中列的名称受大小写敏感限制,因此在构造DataTable的时候应请注意列名要与表一致。
以上没有使用事务,使用事务在性能上会有一定的影响,如果要使用事务,可以设置SqlBulkCopyOptions.UseInternalTransaction。
再另一种写法:
string connectionString = @"Server=Y203-01\SQLEXPRESS;database=InternetDB;uid=sa;pwd=121800;Max Pool Size = 512;";
DateTime time = DateTime.Now;
SqlBulkCopy sqlBC = new SqlBulkCopy(connectionString, SqlBulkCopyOptions.UseInternalTransaction);
sqlBC.DestinationTableName = "Tianya_Users";
DataTable dt = new DataTable("Tianya_Users");
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("UserName", typeof(string));
dt.Columns.Add("PassWord", typeof(string));
dt.Columns.Add("Email", typeof(string));
DirectoryInfo direct = new DirectoryInfo(@"D:\tianya");
string[] userInfo;
string readStr;
int totalNum = 0;
int partNum = 0;
int totalError = 0;
int partError = 0;
foreach (FileInfo NextFile in direct.GetFiles())
{
partNum = 0;
partError = 0;
Console.WriteLine("正在读取文件:" + NextFile.FullName);
StreamReader sr = new StreamReader(NextFile.FullName, Encoding.Default);//必须要指定编码格式,否则中文会乱码,应该可以用UTF-8
while (sr.Peek() > -1)
{
totalNum++;
partNum++;
readStr = sr.ReadLine();
userInfo = readStr.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
try
{
if (userInfo.Length < 1) continue;
if (userInfo.Length < 2)
dt.Rows.Add(0, userInfo[0].Trim(), DBNull.Value, DBNull.Value);
else if (userInfo.Length < 3)
dt.Rows.Add(0, userInfo[0].Trim(), userInfo[1].Trim(), DBNull.Value);
else
dt.Rows.Add(0, userInfo[0].Trim(), userInfo[1].Trim(), userInfo[2].Trim());
}
catch (Exception e1)
{
Console.WriteLine(e1.Message);
totalError++;
partError++;
}
//if (partNum % 100000 == 0) //每读10W条写入到数据库一次
//{
// sqlBC.WriteToServer(dt);
// dt.Clear();
//}
}
sqlBC.WriteToServer(dt);
Console.WriteLine("文件" + NextFile.FullName + "转换完成。本文件共有:" + partNum + " 条数据,其中失败:" + partError + " 条");
dt.Clear();
sr.Close();
}
dt.Dispose();
sqlBC.Close();
Console.WriteLine("转换完成。文件共有:" + totalNum + " 条数据,其中失败:" + totalError + " 条");
Console.WriteLine("共耗时:" + (DateTime.Now - time).TotalSeconds.ToString() + " 秒");