SqlBulkCopy 實現原汁原味複製的注意事項
1. 有標識列的表
1. 1 SqlBulkCopyOptions.KeepIdentity 必須設定!否則會出現複製過去的資料產生標識列發現變化的情況!
1.2 如果原表的標識列即為主鍵, 那按1.1 的設定已足夠。 如果原表無主鍵, 那在複製之前必須先清空原表(truncate table), 否則會出現多個相同的標識值的列!
2. 為NULL值的列
2.1 SqlBulkCopyOptions.KeepNulls 必須設定!否則會出現源資料的欄位為NULL時, 複製過去卻成了預設值!
其它幾個選項的說明與分析:
Default 對所有選項使用預設值。
KeepIdentity
CheckConstraints 請在插入資料的同時檢查約束。預設情況下,不檢查約束。
TableLock 在批量複製操作期間獲取批量更新鎖。如果未指定,則使用行鎖。
KeepNulls 保留目標表中的空值,而不管預設值的設定如何。如果未指定,則空值將由預設值替換(如果適用)。
FireTriggers 指定後,會導致伺服器為插入到資料庫中的行激發插入觸發器。 預設情況下, 是不激發觸發器的……
UseInternalTransaction 如果已指定,則每一批批量複製操作將在事務中發生。 在一個事務中執行,要麼都成功,要麼都不成功
Default 就沒有什麼好說的了, 不要
KeepIdentity 和 KeepNulls 上面已有了, 不再分析。
CheckConstraints
不需要, 因為是現成的資料, 既然已在DB中, 必然是通過了約束檢查的。
TableLock 不需要, 因為複製時兩個庫都需要處於單連線狀態, 不可能有干擾。
FireTriggers
一般就不需要了吧, 畢竟只是複製資料, 而且是現成的資料……
UseInternalTransaction
關係也不大, 反正複製失敗會記錄到自定義的日誌, 失敗了也知道, 重來一次就可以了。
下面是便於測試的程式碼
SQL:
--1. 建資料來源表 IF EXISTS ( SELECT 1 FROM sysobjects WHERE id = OBJECT_ID(N'Table_1') AND OBJECTPROPERTY(id, N'IsUserTable') = 1 ) BEGIN DROP TABLE Table_1 END GO Create Table Table_1( ID INT, [NAME] VARCHAR(50) DEFAULT('xx') ) GO INSERT INTO Table_1 SELECT 1,'a' UNION SELECT 2,'b' UNION SELECT 3,'c' UNION SELECT 4,'d' UNION SELECT 5,'e' UNION SELECT 6,null --2. 建目標表 IF EXISTS ( SELECT 1 FROM sysobjects WHERE id = OBJECT_ID(N'Table_2') AND OBJECTPROPERTY(id, N'IsUserTable') = 1 ) BEGIN DROP TABLE Table_2 END GO Create Table Table_2( ID2 INT, [NAME2] VARCHAR(50) DEFAULT('xx') ) GO --複製時的對應關係 --Tabe_1 ==> Table_2 --ID ==> ID2 --[NAME] ==> [NAME2]
C# 控制檯程式:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Collections;
namespace SqlBulkCopyDemo
{
class Program
{
static void Main(string[] args)
{
string connString = System.Configuration.ConfigurationManager.AppSettings["DCString"];
SqlConnection ConnectionNew = new SqlConnection(connString);
SqlConnection ConnectionOld = new SqlConnection(connString);
bool ExportInfo = System.Configuration.ConfigurationManager.AppSettings["ExportInfo"].ToLower()=="true";
try
{
ConnectionNew.Open();
ConnectionOld.Open();
//1.在舊錶中,用SqlDataReader讀取出資訊
string SQL = "select * from Table_1";
SqlCommand cmd = new SqlCommand(SQL, ConnectionOld);
cmd.CommandTimeout = 7200;
SqlDataReader sdr = cmd.ExecuteReader();
//2.初始化SqlBulkCopy物件,用新的連線作為引數。
SqlBulkCopy bulkCopy = new SqlBulkCopy(ConnectionNew, SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.KeepNulls, null);
bulkCopy.BulkCopyTimeout = 7200;
//3.寫對應關係。如舊錶的People列的資料,對應新表Human列,那麼就寫bulkCopy.ColumnMappings.Add("People","Human")
//如果兩張表的結構一樣,那麼對應關係就不用寫了。
//我是用雜湊表儲存對應關係的,雜湊表作為引數到傳入方法中,key的值用來儲存舊錶的欄位名,VALUE的值用來儲存新表的值
Hashtable ht = new Hashtable();
ht.Add("ID", "ID2");
ht.Add("NAME", "NAME2");//
foreach (string str in ht.Keys)
{
bulkCopy.ColumnMappings.Add(str, ht[str].ToString());
}
//4.設定目標表名
bulkCopy.DestinationTableName = "Table_2";
//額外,可不寫:設定一次性處理的行數。這個行數處理完後,會激發SqlRowsCopied()方法。預設為1
bulkCopy.NotifyAfter = 1;
if (ExportInfo)
{
//額外,可不寫:設定激發的SqlRowsCopied()方法,這裡為bulkCopy_SqlRowsCopied
bulkCopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(bulkCopy_SqlRowsCopied);
}
//OK,開始傳資料!
DateTime dt = DateTime.Now;
Console.WriteLine("開始時間:{0:yyyy-MM-dd HH:mm:ss,ms}", dt);
bulkCopy.WriteToServer(sdr);
DateTime dt2 = DateTime.Now;
Console.WriteLine("結束時間:{0:yyyy-MM-dd HH:mm:ss,ms}", dt2);
double time = dt2.Subtract(dt).TotalMilliseconds;
Console.WriteLine("傳輸完畢!所用時間為:{0}(ms)", time);
Console.Read();
}
catch (Exception ex)
{
Console.Write(ex.Message);
Console.Read();
}
finally
{
ConnectionNew.Close();
ConnectionOld.Close();
}
}
//激發的方法寫在外頭
private static void bulkCopy_SqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
{
//執行的內容。
//這裡有2個元素值得拿來用
//e.RowsCopied, //返回數值型別,表示當前已經複製的行數
//e.Abort, //用於賦值true or false,用於停止賦值的操作
Console.WriteLine("當前已複製的行數:" + e.RowsCopied);
}
}
}
配置檔案:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DCString" value="Data Source=(local);Initial Catalog=db_Study;Persist Security Info=True;User ID=??;Password=??" />
<add key="ExportInfo" value="false" />
</appSettings>
</configuration>