以下内容大部分来自:

http://blog.csdn.net/tjvictor/article/details/4360030

部分内容出自互联网,实验结果为亲测。

最近自己开发一个向数据库中插入大量历史数据的函数库,需要解决一个大数据量插入的效率问题。不用分析,我知道如果采取逐条数据插入的方式,那么效率肯定很低,光是那么多循环就知道很慢了。于是乎,我找到了上篇博客,知道了BulkCopy和TVPs方式。为了更好的了解其效率,我自己动手亲测了一下效果,测试的数据库位于本机。

(1)方式1:循环插入

        public static void NormalInerst(String connString)
{
Console.WriteLine("使用NNormalInerst方式:");
Stopwatch sw = new Stopwatch();
SqlConnection sqlConn = new SqlConnection(connString);
SqlCommand sqlCmd = new SqlCommand();
sqlCmd.CommandText = String.Format("insert into BulkTestTable(Id,UserName,Pwd)values(@p0,@p1,@p2)");
sqlCmd.Parameters.Add("@p0", SqlDbType.Int);
sqlCmd.Parameters.Add("@p1", SqlDbType.NVarChar);
sqlCmd.Parameters.Add("@p2", SqlDbType.VarChar);
sqlCmd.CommandType = CommandType.Text;
sqlCmd.Connection = sqlConn;
sqlConn.Open();
try
{
for (int i = , j = ; i < ; ++i )
{
for (j = i * ; j < (i + ) * ; ++j )
{
sqlCmd.Parameters["@p0"].Value = j;
sqlCmd.Parameters["@p1"].Value = String.Format("User-{0}", i * j);
sqlCmd.Parameters["@p2"].Value = String.Format("Pwd-{0}", i * j);
sw.Start();
sqlCmd.ExecuteNonQuery();
sw.Stop();
} Console.WriteLine("第{0}次插入{1}条数据耗时:{2}", (i + ), dataScale, sw.ElapsedMilliseconds);
sw.Reset();
}
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
sqlConn.Close();
}
}

该方式的效率极低,运行时间很长,我这里就不给出结果了,有兴趣可以自己粘贴试一下。PS:其中的数据规模应该是dataScale而不是10000,不过总是还是慢。

(2)方式2:使用BulkCopy

        public static void BulkInerst(String connString)
{
Console.WriteLine("使用BulkInerst方式:");
Stopwatch sw = new Stopwatch(); String strDel = "delete from BulkTestTable";
float millTime = ;
for (int multiply = ; multiply < ; multiply++)
{
DataTable dt = GetTableSchema();
for (int count = multiply * dataScale; count < (multiply + ) * dataScale; count++)
{
DataRow r = dt.NewRow();
r[] = count;
r[] = string.Format("User-{0}", count * multiply);
r[] = string.Format("Pwd-{0}", count * multiply);
dt.Rows.Add(r);
} SqlConnection sqlConn = new SqlConnection(connString);
SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConn);
bulkCopy.DestinationTableName = "BulkTestTable";
bulkCopy.BatchSize = dt.Rows.Count; sw.Reset();
sw.Start();
try
{
sqlConn.Open();
if (dt != null && dt.Rows.Count != )
bulkCopy.WriteToServer(dt);
}
catch (Exception ex)
{
throw ex;
}
finally
{
sqlConn.Close();
if (bulkCopy != null)
bulkCopy.Close();
}
sw.Stop(); Console.WriteLine("第{0}次插入{1}条数据耗时:{2}", (multiply + ), dataScale, sw.ElapsedMilliseconds);
millTime += sw.ElapsedMilliseconds;
}
Console.WriteLine("总耗时:{0}毫秒,平均耗时:{1}毫秒", millTime, millTime / );
SqlConnection sqlConn2 = new SqlConnection(connString);
SqlCommand sqlCmd = new SqlCommand(strDel, sqlConn2);
try
{
sqlConn2.Open();
sqlCmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
sqlConn2.Close();
}
Console.WriteLine("Done!");
}

(3)方式3:使用TVPs

        public static void TVPsInerst(String connString)
{
Console.WriteLine("使用TVPsInerst方式:");
Stopwatch sw = new Stopwatch();
SqlConnection sqlConn = new SqlConnection(connString);
String strSQL = "insert into BulkTestTable (Id,UserName,Pwd)" +
" SELECT nc.Id, nc.UserName,nc.Pwd" +
" FROM @NewBulkTestTvp AS nc";
String strDel = "delete from BulkTestTable";
float millTime = ; for (int multiply = ; multiply < ; multiply++)
{
DataTable dt = GetTableSchema();
for (int count = multiply * dataScale; count < (multiply + ) * dataScale; count++)
{
DataRow r = dt.NewRow();
r[] = count;
r[] = string.Format("User-{0}", count * multiply);
r[] = string.Format("Pwd-{0}", count * multiply);
dt.Rows.Add(r);
} sw.Reset();
sw.Start();
SqlCommand cmd = new SqlCommand(strSQL, sqlConn);
SqlParameter catParam = cmd.Parameters.AddWithValue("@NewBulkTestTvp", dt);
catParam.SqlDbType = SqlDbType.Structured;
catParam.TypeName = "dbo.BulkUDT";
try
{
sqlConn.Open();
if (dt != null && dt.Rows.Count != )
{
cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
sqlConn.Close();
}
sw.Stop(); Console.WriteLine("第{0}次插入{1}条数据耗时:{2}", (multiply + ), dataScale, sw.ElapsedMilliseconds);
millTime += sw.ElapsedMilliseconds;
}
Console.WriteLine("总耗时:{0}毫秒,平均耗时:{1}毫秒", millTime, millTime / );
SqlCommand sqlCmd = new SqlCommand(strDel, sqlConn);
try
{
sqlConn.Open();
sqlCmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
sqlConn.Close();
}
Console.WriteLine("Done!");
}

这里TVPs方式需要利用Visual Studio 2008采用的自定义数据表类型,这是一个比较新的东西。这里补充几个类型和函数,主要是为了检测数据库中是否存在数据表和数据表类型,如果不存在则进行创建。补充代码如下:

        public enum CheckType
{
isTable = ,
isType
} protected static int dataScale = ; public static bool CheckExistsObject(String connString, String objectName, CheckType type)
{
String strSQL = "select COUNT(1) from sys.sysobjects where name='" + objectName + "'";
switch (type)
{
case CheckType.isTable:
strSQL = "select COUNT(1) from sys.sysobjects where name='" + objectName + "'";
break;
case CheckType.isType:
strSQL = "select COUNT(1) from sys.types where name='" + objectName + "'";
break;
default:
break;
}
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(strSQL, conn);
int result = Convert.ToInt32(cmd.ExecuteScalar());
if ( == result)
{
return false;
}
} return true;
} public static bool CreateObject(String connString, String objectName, CheckType type)
{
String strSQL = "";
switch (type)
{
case CheckType.isTable:
strSQL = "Create table " + objectName + " (Id int primary key, UserName nvarchar(32), Pwd varchar(16))";
break;
case CheckType.isType:
strSQL = "CREATE TYPE " + objectName + " AS TABLE (Id int, UserName nvarchar(32), Pwd varchar(16))";
break;
default:
break;
}
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(strSQL, conn);
cmd.ExecuteNonQuery();
} return true;
}
public static DataTable GetTableSchema()
{
DataTable dt = new DataTable();
dt.Columns.AddRange(new DataColumn[]{
new DataColumn("Id",typeof(int)),
new DataColumn("UserName",typeof(string)),
new DataColumn("Pwd",typeof(string))}); return dt;
}

调用的方式就很好说了,参见如下测试代码:

        public static void Main(string[] args)
{
String conString = "Persist Security Info=False;User ID=sa;Password=scbj123!@#;Initial Catalog=testGR;Server=KLH-PC";
String strType = "BulkUDT";
String strTable = "BulkTestTable";
if (!CheckExistsObject(conString, strType, CheckType.isType))
{
Console.WriteLine("类型{0}不存在", strType);
if (CreateObject(conString, strType, CheckType.isType))
{
Console.WriteLine("类型{0}创建成功!", strType);
}
} if (!CheckExistsObject(conString, strTable, CheckType.isTable))
{
Console.WriteLine("表格{0}不存在", strTable);
if (CreateObject(conString, strTable, CheckType.isTable))
{
Console.WriteLine("表格{0}创建成功!", strTable);
}
}
Console.WriteLine("=================================================="); //NormalInerst(conString);
BulkInerst(conString);
TVPsInerst(conString); Console.ReadKey();
}

-------------------------------------------------------------------------------------------------

直接看效果对比:

<1>第一次运行

<2>第二次和第三次运行

这里考虑到了SQL Server自身缓存的原因,所以进行了多次测试,不过数据量没有变。可以从上述结果中看出:TVPs方式不愧是新出的啊,一代更比一代强!

C# & SQL Server大数据量插入方式对比的更多相关文章

  1. sql server 2005 大数据量插入性能对比

    sql server 2005大数据量的插入操作 第一,写个存储过程,传入参数,存储过程里面是insert操作, 第二,用System.Data.SqlClient.SqlBulkCopy实例方法, ...

  2. [转]Sql server 大数据量分页存储过程效率测试附代码

    本文转自:http://www.cnblogs.com/lli0077/archive/2008/09/03/1282862.html 在项目中,我们经常遇到或用到分页,那么在大数据量(百万级以上)下 ...

  3. SQL Server 大数据量insert into xx select慢的解决方案

    最近项目有个需求,把一张表中的数据根据一定条件增删改到另外一张表.按理说这是个很简单的SQL.可是在实际过程中却出现了超级长时间的执行过程. 后来经过排查发现是大数据量insert into xx s ...

  4. SQL Server 大数据量批量插入

    private void AddShuJu_Click(object sender, RoutedEventArgs e) { Stopwatch wath = new Stopwatch(); wa ...

  5. SQL Server 大数据量分页建议方案

    简单的说就是这个 select top(20) * from( select *, rowid = row_number() over(order by xxx) from tb with(noloc ...

  6. mysql/oracle jdbc大数据量插入优化

    10.10.6  大数据量插入优化 在很多涉及支付和金融相关的系统中,夜间会进行批处理,在批处理的一开始或最后一般需要将数据回库,因为应用和数据库通常部署在不同的服务器,而且应用所在的服务器一般也不会 ...

  7. SQL优化-大数据量分页优化

    百万数据量SQL,在进行分页查询时会出现性能问题,例如我们使用PageHelper时,由于分页查询时,PageHelper会拦截查询的语句会进行两个步骤 1.添加 select count(*)fro ...

  8. sql server 大数据, 统计分组查询,数据量比较大计算每秒钟执行数据执行次数

    -- 数据量比较大的情况,统计十分钟内每秒钟执行次数 ); -- 开始时间 ); -- 结束时间 declare @num int; -- 结束时间 set @begintime = '2019-08 ...

  9. SQL Server 大数据搬迁之文件组备份还原实战

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 解决方案(Solution) 搬迁步骤(Procedure) 搬迁脚本(SQL Codes) ...

随机推荐

  1. nginx php-cgi php

    /*************************************************************************** * nginx php-cgi php * 说 ...

  2. MyBatis日期有坑

    使用MyBatis时,可能会遇到日期格式的时间段问题,当数据库的时间为DATE类型时,MyBatis的jdbcType应该使用DATE,否则,有时间会出现莫名的数据找不到的问题,具体原因,可以查看源码 ...

  3. 添加mongodb支持

    最近花了些时间,将引擎的存储换成了mongodb. 私下,我觉得现有的存储机制极为落后.现在写数据的操作交由单独的进程完成,该进程兼当数据缓冲与持续化数据的责任.此次引擎的更换,只是简单的利用mong ...

  4. 我看的公开课系列--《TED:对无知的追求》 by stuart firestein

    http://open.sina.com.cn/course/id_1047/ What scientists do is not just collect data and collect fact ...

  5. sqlserver mdf向上兼容附加数据库(无法打开数据库 'xxxxx' 版本 611。请将该数据库升级为最新版本。)

    最近工作中有一个sqlserver2005版本的mdf文件,还没有log文件,现在需要 附加到sqlserver2012,经过网上一顿搜索,把完整的过程奉上,供大家参考 首先创建数据库 再设置数据库的 ...

  6. 1130-host ... is not allowed to connect to this MySql server 开放mysql远程连接 不使用localhost

    报错:1130-host ... is not allowed to connect to this MySql server 解决方法: 1. 改表法. 可能是你的帐号不允许从远程登陆,只能在loc ...

  7. Java 性能优化实战记录(1)---定位并分析耗cpu最多的线程

    1) jps    列出相关的java进程, 以及对应的pid    也可以使用如下命令来尝试    ps aux | grep java --color 2) top -Hp <pid> ...

  8. js 如何判断页面里的某个值改变

    程序未改变变量的值前新增全局变量:var oldDivValue = document.getElementById("divid").innerHTML; 程序在改变变量值后执行 ...

  9. html--整站制作

    1.样式初置 body,div,ul,ol,h1,h2,h3,h4,h5,p,form,input,textarea,select{margin:0;padding:0;} li{list-style ...

  10. JavaWeb学习记录(二十)——Model1模式(javaBean+jsp)实现简单计算器案例

    ¨JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,它们分别为: ¨<jsp:useBean>标签:用于在JSP页面中查找或实例化一个JavaBean组件. ¨< ...