Sql-Server

不顯示從文件導入的特殊字元

  • November 18, 2019

該場景是一個 SQL Server 實例,一個主要使用 BULK INSERT 操作提供數據的數據庫,並且插入的一些文本包含特殊字元,例如ñ因為我在西班牙環境中工作。

所以,在最初的小測試之後,我意識到當我執行一個簡單的 時這些特殊字元沒有正確顯示select,所以我開始檢查我能想到的一切:

  • 文件編碼:要批量插入的文件具有正確的編碼:ANSI
  • 數據庫編碼:數據庫具有正確的編碼(謝天謝地):select collation_name from sys.databases where name='DBNAME';結果SQL_Latin1_General_CP1_CI_AS
    • Latin1:使用的字元集。這很適合我
    • 將軍:這裡沒什麼有趣的
    • CP1:這意味著它使用程式碼頁 1,簡而言之,這意味著程式碼頁 1252 <=> 用於編碼 WIN-1252 的程式碼頁,與 Latin1 非常相似
    • CI:不區分大小寫
    • AS:對口音敏感,所以 á 與 a 不同
  • 將數據導出到文件並檢查:文件編碼為ANSI ,但數據顯示不正確,沒有特殊字元,而是發現了一些其他字元,使文本難以閱讀。

通過這些測試,我得出的結論是數據沒有正確儲存,這就是它沒有正確顯示和導出的原因。我在網際網路上找到的幾乎所有解決方案都建議使用字元串數據類型欄位nvarchar而不是varchar字元串數據類型欄位,但這並不能解決這種情況。是什麼破壞了我的插入?

如果字元導入不正確,則以下一個(或兩個)區域存在問題,因為這實際上是一個兩步過程:

  1. BCP / BULK INSERT 不知道文件是如何編碼的並且解釋不正確
  2. 目標列是VARCHAR(或CHAR{or TEXT,但不要使用TEXT} )並且該目標(不是數據庫)的排序規則使用的程式碼頁沒有錯誤導入字元的映射。

這裡需要注意的是,導入表所在的數據庫的預設排序規則是不相關的。這裡唯一重要的排序規則是要導入的每個特定字元串列的排序規則。並且每個字元串列的排序規則都可以不同,它們都不需要與數據庫的預設排序規則相同。通常讓數據庫中大多數列的排序規則與數據庫的預設值匹配,因為這將是創建新表和列時使用的排序規則,而不是通過COLLATE關鍵字指定排序規則。

第 1 步:文件編碼

BCP / BULK INSERT(或大多數其他讀取文件的程式碼)將不知道文件正在使用什麼編碼,除非文件使用為數不多的具有字節順序標記 (BOM)的編碼之一。但是擴展的 ASCII 編碼不使用字節順序標記,因此不能以程式方式確定(至少不能確定)。使用擴展 ASCII 編碼時,您需要指定程式碼頁,否則將假定為預設值。

根據bcp Utility的 MSDN 頁面,在**-C**選項下:

OEM -> 客戶端使用的預設程式碼頁。如果未指定**-C** ,這是使用的預設程式碼頁。

您可以通過打開命令提示符並執行modeor來確定預設程式碼頁chcp(我更喜歡chcp它,因為如果您傳入一個值,它還允許更改程式碼頁)。

如果您的預設程式碼頁是 850,但文件是使用 Windows-1252 Latin1 (ANSI) 編碼保存的,那麼解釋文件很容易出現問題,因為這兩個程式碼頁之間的字元映射不同。這與 SQL Server 沒有任何關係。

ñ字元在程式碼頁 1252 上的值為 241。但是,在程式碼頁 850 上,同一字元的值為 164。不管其他任何內容,該文件都是一系列字節,其中一個字節具有十進制值241(因為當它被保存時,它被告知使用程式碼頁 1252 確定ñ需要儲存為 241)。現在,當 BCP 讀取文件時,如果它使用預設的 MS-DOS 程式碼頁 850,則相同的字節值 241 映射到 character ±。如果您要通過開關指定一個ACP1252(相同的東西)的程式碼頁-C,那麼 BCP 將知道值 241 的字節實際上是ñ. 或者,您可以指定程式碼頁 1255(Windows 希伯來語),然後 BCP / BULK INSERT 會將相同的字節值 241 解釋為字元ס.

第 2 步:目標列數據類型和排序規則

一旦 BCP / BULK INSERT(或任何客戶端應用程序)讀取數據,它就會作為這些映射存在,而不僅僅是基本字節值。讀入 BCP / BULK INSERT 的任何字元都將作為該字元儲存在目標列中,只要該字元可以映射到目標數據類型和排序規則中。NVARCHARNCHARNTEXT(但不要使用)的目標數據類型NTEXT可以保存所有字元,因此 Collat​​ion 是什麼並不重要。但是,如果目標數據類型是VARCHAR, CHAR, 或TEXT然後排序規則將確定程式碼頁,該程式碼頁又確定字元映射。如果目標數據類型是最後提到的 3 種之一,並且使用與用於文件的相同程式碼頁關聯的排序規則,那麼一切都應該正常工作。或者,如果 Collat​​ion 與不同的程式碼頁相關聯,則將嘗試映射character,而不是 byte 值。

意思是,如果 BCP / BULK INSERT 正在使用程式碼頁 1252 和字元ñ(程式碼頁 1252 上的值 241),那麼如果您將其導入到VARCHAR具有排序規則的列中SQL_Latin1_General_CP850_CI_AS——它使用程式碼頁 850——然後您將看到一個字元ñ(相同的字元,但程式碼頁 850 上的值為 164)而不是±,它在程式碼頁 850 上具有相同的 241 值。但是,如果您導入到VARCHAR具有排序規則的列中Hebrew_CI_AS– 它使用程式碼頁1255 - 然後您將看到?而不是ס(程式碼頁 1255 上的值 241),因為在程式碼頁 1255 上沒有映射ñ

引用自:https://dba.stackexchange.com/questions/159564