SQLServer中批量插入数据方式的性能对比
公司技术背景:数据库访问类(xxx.DataBase.Dll)调用存储过程实现数据库的访问。 1.创建表。 为了简单,表中只有一个字段,如下图所示: 2.创建表值参数类型 我们打开查询分析器,然后在查询分析器中执行下列代码: Create Type PassportTableType as Table ( PassportKey nvarchar(50) ) 执行成功以后,我们打开企业管理器,按顺序依次展开下列节点--数据库、展开可编程性、类型、用户自定义表类型,就可以看到我们创建好的表值类型了如下图所示: 说明我们创建表值类型成功了。 3.编写存储过程 存储过程的代码为: USE [TestInsert] 可能在查询分析器中,智能提示会提示表值类型有问题,会出现红色下划线(见下图),不用理会, 4.编写代码调用存储过程。 三种数据库的插入方式代码如下,由于时间比较紧,代码可能不那么易读,特别代码我加了些注释。 using System; using System.Diagnostics; using System.Data; using System.Data.SqlClient; using com.DataAccess; namespace ConsoleAppInsertTest { class Program { static string connectionString = SqlHelper.ConnectionStringLocalTransaction; //数据库连接字符串 static int count = 1000000; //插入的条数 static void Main(string[] args) { //long commonInsertRunTime = CommonInsert(); //Console.WriteLine(string.Format("普通方式插入{1}条数据所用的时间是{0}毫秒",commonInsertRunTime,count)); long sqlBulkCopyInsertRunTime = SqlBulkCopyInsert(); Console.WriteLine(string.Format("使用SqlBulkCopy插入{1}条数据所用的时间是{0}毫秒",sqlBulkCopyInsertRunTime,count)); long TVPInsertRunTime = TVPInsert(); Console.WriteLine(string.Format("使用表值方式(TVP)插入{1}条数据所用的时间是{0}毫秒",TVPInsertRunTime,count)); } /// <summary> /// 普通调用存储过程插入数据 /// </summary> /// <returns></returns> private static long CommonInsert() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); string passportKey; for (int i = 0; i < count; i++) { passportKey = Guid.NewGuid().ToString(); SqlParameter[] sqlParameter = { new SqlParameter("@passport",passportKey) }; SqlHelper.ExecuteNonQuery(connectionString,CommandType.StoredProcedure,"CreatePassport",sqlParameter); } stopwatch.Stop(); return stopwatch.ElapsedMilliseconds; } /// <summary> /// 使用SqlBulkCopy方式插入数据 /// </summary> /// <param name="dataTable"></param> /// <returns></returns> private static long SqlBulkCopyInsert() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); DataTable dataTable = GetTableSchema(); string passportKey; for (int i = 0; i < count; i++) { passportKey = Guid.NewGuid().ToString(); DataRow dataRow = dataTable.NewRow(); dataRow[0] = passportKey; dataTable.Rows.Add(dataRow); } SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(connectionString); sqlBulkCopy.DestinationTableName = "Passport"; sqlBulkCopy.BatchSize = dataTable.Rows.Count; SqlConnection sqlConnection = new SqlConnection(connectionString); sqlConnection.Open(); if (dataTable!=null && dataTable.Rows.Count!=0) { sqlBulkCopy.WriteToServer(dataTable); } sqlBulkCopy.Close(); sqlConnection.Close(); stopwatch.Stop(); return stopwatch.ElapsedMilliseconds; } private static long TVPInsert() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); DataTable dataTable = GetTableSchema(); string passportKey; for (int i = 0; i < count; i++) { passportKey = Guid.NewGuid().ToString(); DataRow dataRow = dataTable.NewRow(); dataRow[0] = passportKey; dataTable.Rows.Add(dataRow); } SqlParameter[] sqlParameter = { new SqlParameter("@TVP",dataTable) }; SqlHelper.ExecuteNonQuery(connectionString,"CreatePassportWithTVP",sqlParameter); stopwatch.Stop(); return stopwatch.ElapsedMilliseconds; } private static DataTable GetTableSchema() { DataTable dataTable = new DataTable(); dataTable.Columns.AddRange(new DataColumn[] { new DataColumn("PassportKey") }); return dataTable; } } } 比较神秘的代码其实就下面这两行,该代码是将一个dataTable做为参数传给了我们的存储过程。简单吧。 SqlParameter[] sqlParameter = { new SqlParameter("@TVP",dataTable) }; 5.测试并记录测试结果 第一组测试,插入记录数1000 第二组测试,插入记录数10000 第三组测试,插入记录数1000000 通过以上测试方案,不难发现,技术方案二的优势还是蛮高的。无论是从通用性还是从性能上考虑,都应该是 优先被选择的,还有一点,它的技术复杂度要比技术方案三要简单一些, 设想我们把所有表都创建一遍表值类型,工作量还是有的。因此,我依然坚持我开始时的决定, 向公司推荐使用第二种技术方案。 写到此,本文就算完了,但是对新技术的钻研仍然还在不断继续。要做的东西还是挺多的。 为了方便大家学习和交流,代码文件已经打包并上传了,欢迎共同学习探讨。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |