使用C#在SQL Server上的临时表中插入3万行的最快方法

我试图找出如何使用c#在SQL Server的临时表中提高插入性能。 有些人说我应该使用SQLBulkCopy但是我必须做错了,因为它似乎比简单地构建一个SQL插入字符串慢得多。

我使用SQLBulkCopy创建表的代码如下:

public void MakeTable(string tableName, List ids, SqlConnection connection) { SqlCommand cmd = new SqlCommand("CREATE TABLE ##" + tableName + " (ID int)", connection); cmd.ExecuteNonQuery(); DataTable localTempTable = new DataTable(tableName); DataColumn id = new DataColumn(); id.DataType = System.Type.GetType("System.Int32"); id.ColumnName = "ID"; localTempTable.Columns.Add(id); foreach (var item in ids) { DataRow row = localTempTable.NewRow(); row[0] = item; localTempTable.Rows.Add(row); localTempTable.AcceptChanges(); } using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)) { bulkCopy.DestinationTableName = "##" + tableName; bulkCopy.WriteToServer(localTempTable); } } 

这样我的插入需要很长时间才能运行。 我让插件以另一种方式更快地工作:

我创建了insert位作为字符串,并在我的SQL create temp table语句中加入了它:

创建插入字符串:

 public string prepareInserts(string tableName, List ids) { List inserts = new List(); var total = ids.Select(p => p).Count(); var size = 1000; var insert = 1; var skip = size * (insert - 1); var canPage = skip  string.Format("({0})", p)) .Skip(skip) .Take(size) .ToArray())); insert++; skip = size * (insert - 1); canPage = skip < total; } string joinedInserts = String.Join("\r\n", inserts.ToArray()); return joinedInserts; } 

创建查询后在SQL语句中使用它们:

 inserts = prepareInserts(tableName, ids); var query = @"IF EXISTS ( SELECT * FROM tempdb.dbo.sysobjects WHERE ID = OBJECT_ID(N'tempdb..##" + tableName + @"') ) BEGIN DELETE FROM ##" + tableName + @" END ELSE BEGIN CREATE TABLE ##" + tableName + @" (ID int) END " + inserts; var command = new SqlCommand(query, sqlConnection); ... 

自从我看到有人告诉我(在堆栈交换机上https://dba.stackexchange.com/questions/44217/fastest-way-to-insert-30-thousand-rows-in-sql-server/44222?noredirect= 1#comment78137_44222 )我应该使用SQLBulkCopy,这会更快我相信我应该改进我的方式。 因此,如果有人可以建议我如何改进我的SQLBulkCopy代码或告诉我是否有一个更好的插入语句可以提高我的应用程序的性能,这将是伟大的。

你的问题可能出在localTempTable.AcceptChanges(); 因为它提交了您的更改。
如果你做下一个,我认为它会运行得更快

  foreach (var item in ids) { DataRow row = localTempTable.NewRow(); row[0] = item; localTempTable.Rows.Add(row); } localTempTable.AcceptChanges(); using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)) { bulkCopy.DestinationTableName = "##" + tableName; bulkCopy.WriteToServer(localTempTable); } 

从MSDN – DataSet.AcceptChanges

提交自加载或自上次调用AcceptChanges以来对此DataSet所做的所有更改。

我自己使用StopWatch对象运行此代码来测量时间。 这是每次迭代中的AcceptChanges变得缓慢。

 public void MakeTable(string tableName, List ids, SqlConnection connection) { SqlCommand cmd = new SqlCommand("CREATE TABLE ##" + tableName + " (ID int)", connection); cmd.ExecuteNonQuery(); DataTable localTempTable = new DataTable(tableName); DataColumn id = new DataColumn(); id.DataType = System.Type.GetType("System.Int32"); id.ColumnName = "ID"; localTempTable.Columns.Add(id); System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch(); sw1.Start(); foreach (var item in ids) { DataRow row = localTempTable.NewRow(); row[0] = item; localTempTable.Rows.Add(row); } localTempTable.AcceptChanges(); long temp1 = sw1.ElapsedMilliseconds; sw1.Reset(); using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)) { bulkCopy.DestinationTableName = "##" + tableName; bulkCopy.WriteToServer(localTempTable); } long temp2 = sw1.ElapsedMilliseconds; } 

当AccpetChanges在foreach循环内时的结果

在此处输入图像描述

当它不是

在此处输入图像描述

差异是3个数量级:)

使用IDataReader ,它将运行得更快

而不是cmd.ExecuteNonQuery(); 执行

 cmd.ExecuteReader()