批量復制工具(BCP)是SQL Server主要的命令行工具之一,使用非常方便, 它也是SQL Server導入導出海量數據的方式。但是DBA應注意BCP存在幾項限制, 本文作者通過自身經歷總結了一些主要的問題表現。
1、沒有對UTF-8的支 持
SQL Server有對Unicode的本地支持,使用過nvarchar和ntext字段類型 的任何人都知道。它通過映射每個字符為雙字節實體來內部處理Unicode。如果你 只是處理SQL Server實例之間的數據,那麼不會有任何問題,因為它們都以相同 的方式存儲。
不過,如果你試圖使用BCP從把Unicode導出為UTF-8的數據 來源導入數據,那事情就有點復雜了。UTF-8是Unicode的一種子變體,專門設計 支持與八位ASCII文本的向後兼容,所以默認使用八位ASCII編碼的網頁、電子郵 件和其它格式可以用於存儲Unicode數據。
如果你從UTF-8源導出數據,不 要指望對這些數據使用BCP;它一直不支持UTF-8。你必須考慮數據問題,以完整雙 字節Unicode導出使數據形成可接受格式。具有諷刺意味的是,另一個普通的編碼 可以通過“-C”開關(ISO 1252,ANSI/微軟公司Windows)被BCP接受。不過,就 整體而言,你最好把數據導出為雙字節Unicode,以保持對BCP的最大兼容性,尤 其是如果你處理的數據可能包含與ASCII不兼容的字符。
2、注意導出的行 順序
使用BCP通過查詢導出的數據對於導出順序遵守相同的規則,會應用 於任何其它情況的查詢。換句話說,如果你的查詢沒有明確的“ORDER BY”從句 ,你獲得的數據看起來就是完全任意的順序。它通常是基於隱含索引中的順序形 成的,但是我已經學會甚至連經驗法則也不相信了——尤其是如果該查詢在多個 表之間執行“JOIN”或者一些其它聚合函數。
數據是按什麼順序導出的通 常並不重要,但是數據以什麼順序導入是非常關鍵的。如果你使用的數據庫是後 來導入行的正確性決定於早先存在的行,而且你是批量導入數據的話,那麼導出 的順序就很重要,你需要相應地建立你的BCP語句。這一點似乎顯而易見,但是我 經常驚訝有那麼多人,甚至包括一些資深的SQL Server專家都沒有意識到這一點 。
3、從BCP激活的存儲過程不能接收參數
如果你使用帶有參數的 存儲過程,作為BCP動作Transact-SQL(T-SQL)語句的一部分,幾乎可以肯定它 不能用,而且會在命令行拋出函數順序錯誤。
當T-SQL語句傳遞給BCP時, 它將被使用“SET FMTONLY ON”機制進行分析,來判斷結果集的柱狀格式。這意 味著動態構造語句(比如帶參數的存儲過程)將不能正確分析,而且也不能在BCP 下編譯。
如果你想解決這個問題,有幾種方法可以選擇:
創建不帶任何參數的存儲過程,用問號激活存儲過程並傳入需要的參數(可能 通過數據源而不是命令行接收參數)。
用sqlcmd替代BCP。
MSDN博客中提到了一個處理技巧,需要使用稱為“openrowset”的技巧。如果 你通過“OPENROWSET ”函數運行“SELECT”,你可以以臨時方式傳遞一個T-SQL 語句,從而解決調用帶參數存儲過程的限制。然而,這種處理技巧也有局限:例 如,與語句連接時不應該使用,因為運行會對數據庫造成消極變化,而且該語句 可能需要運行不止一次。
4、導入時要注意表定義
當你使用BCP從一個SQL Server源導出數據, 並導入到另一個SQL Server時,你導出時的列定義和導入時的列定義必須相匹配 。這也包括諸如NULL或者NOT NULL這類定義,在目標表缺少它們會引起靜默數據 損壞。
5、在目標數據庫上的觸發器不能被BCP觸發
不管什麼時候 運行導入操作,BCP的本地行為在目標數據庫上都會禁用觸發器。因為BCP導入操 作通常很大,如果按默認啟用觸發器的話,導入操作會很混亂。因此,你需要在 BCP上使用命令選項“-h FIRE_TRIGGERS”,這樣觸發器才會被觸發。
要 注意,當選項啟用時,觸發器會為每個批量操作運行一次,——也就是說,每次 你運行BCP時執行一次。另外還要注意,在SQL Server 2005和以後的版本中,觸 發器使用了“行版本”,在導入操作時用tempdb來存儲行版本信息。如果你的 tempdb不能容納觸發器生成的大量數據湧入,該操作將異常終止。
6、BCP 不能給本地附加文件輸出
如果你使用BCP導出數據到文件,該文件必須是 新創建的。你不能選擇現存文件,並把導出結果追加到文件。幸運的是,解決辦 法並不困難,您可以簡單地導出到任何多個文件,然後使用COPY命令來整合這些 結果。命令如下:
COPY export1.dat + export2.dat export.dat