在上一篇文章不同版本的SQL Server之間數據導出導入的方法及性能比較中比較了5種方法進行數據遷移的效率,最后發現使用SqlBulkCopy具有極高的性能。
在文章的回復中,園友溫景良(Jason)提到:sqlbulkcopy如果加上事務呢,速度應該快些吧!
當時我一想,SqlBulkCopy可以包含在事務里面執行嗎?因為我記得SqlBulkCopy是基於BCP且不寫log的。所以也沒敢倉促回復。
然后我去查了下,的確是可以包含在事務中執行的,且提供了兩種方法。
使用UseInternalTransaction
在構造函數SqlBulkCopy(String, SqlBulkCopyOptions)里面有SqlBulkCopyOptions的選項,有如下可選項。
我們看到可選項分別有保持Identity鍵,檢查約束,是否鎖表,保持null值,觸發觸發器,使用事務。
所以如果只是想把SqlBulkCopy包含在事務中,只要打開此屬性即可。
使用System.Data.SqlClient.SqlTransaction
使用自定義的事務,將SqlBulkCopy和其他操作一起包含在一個事務中。
這種方法只要使用另一個構造函數即可:SqlBulkCopy(SqlConnection, SqlBulkCopyOptions, SqlTransaction)。
需要注意的是:當打開了UseInternalTransaction選項后,就不可以和自定義事務一起使用了,否則會拋出InvalidArgumentException。
事務影響性能?
下面是使用第一種開啟事務的測試代碼,沒有使用事務的代碼參見前一篇文章。
使用UseInternalTransaction的代碼。
2 using System.Data;
3 using System.Data.SqlClient;
4
5 namespace BulkInsert
6 {
7 static class Program
8 {
9 static void Main()
10 {
11 DateTime dateTimeStart = DateTime.Now;
12 Console.WriteLine( " Start Insert: " + dateTimeStart.ToString( " HH:mm:ss fff "));
13 // 導入導出的數據庫連接
14 // SqlConnection connectionDestination = new SqlConnection();
15 SqlConnection connectionSource = new SqlConnection( " Server =.; User ID=sa; Password=password; Initial CataLog=ExportDataDemo_Source; ");
16
17 // 實例化一個SqlBulkCopy
18 var bulker = new SqlBulkCopy( " Server =.; User ID=sa; Password=password; Initial CataLog=ExportDataDemo_Destination; ", SqlBulkCopyOptions.UseInternalTransaction) { DestinationTableName = " DEMOTABLE ", BulkCopyTimeout = 600 };
19
20 // 獲取源數據庫的數據
21 SqlCommand sqlcmd = new SqlCommand( " SELECT * FROM DEMOTABLE ", connectionSource);
22 SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlcmd);
23 DataTable dataTableSource = new DataTable();
24 sqlDataAdapter.Fill(dataTableSource);
25
26 // 可以重新定義字段的Mapping關系
27 // SqlBulkCopyColumnMapping sqlBulkCopyColumnMapping = new SqlBulkCopyColumnMapping("COL1", "NEW_COL1");
28 // bulker.ColumnMappings.Add(sqlBulkCopyColumnMapping);
29 // connectionDestination.Open();
30 bulker.WriteToServer(dataTableSource);
31 bulker.Close();
32 DateTime dateTimeEnd = DateTime.Now;
33 Console.WriteLine( " Insert Ending: " + dateTimeEnd.ToString( " HH:mm:ss fff "));
34 }
35 }
36 }
Source表同樣還是10萬的數據,每遷移一次后使用TRUNCATE TABLE清空數據,測試三次取平均值。
截圖如下。
A:不使用事務
B:使用事務
最終對比數據。單位為秒,忘記加在圖表上面了 :)
特定的測試環境與數據,測試結果僅供參考,歡迎交流、討論。