將 Unicode 轉換為非 Unicode / NVARCHAR 為 VARCHAR 時的自動翻譯
Unicode 程式碼點 9619 是一個稱為“暗色”的字元:(
▓
http://unicode-table.com/en/search/?q=9619 )。使用
SQL_Latin1_General_CP1_CI_AS
排序規則和 1252 程式碼頁,我希望將該 Unicode 字元轉換/轉換為非 Unicode 數據類型會導致一個問號 (?
),因為程式碼頁 1252 似乎不包含此字元,這似乎是 SQL Server 的無法進行轉換時的行為。所以我的問題是:為什麼 SQL Server 將此字元轉換為 ASCII 碼 166,即 “Pipe, Broken vertical bar”:
¦
?SELECT NCHAR(9619), CAST(NCHAR(9619) AS CHAR(1)), ASCII(CAST(NCHAR(9619) AS CHAR(1)))
為什麼 SQL 將 Unicode 9619 轉換為 ASCII 碼 166?
SQL Server 在這裡沒有使用任何特殊的自定義邏輯;它使用標準作業系統服務來執行轉換。
具體來說,SQL Server 類型和表達式服務 ( )
sqlTsEs
呼叫WideCharToMultiByte
.kernel32.dll
SQL Server 將輸入參數設置為WideCharToMultiByte
使常式執行“快速轉換”。這比在不存在直接翻譯時請求使用特定的預設字元要快。快速翻譯依賴於目標程式碼頁來為任何不匹配的字元執行最佳匹配映射,正如Martin Smith在對問題的評論中提供的連結中所述:
最佳匹配策略因不同的程式碼頁而異,並且沒有詳細記錄。
當為快速翻譯設置輸入參數時,
WideCharToMultiByte
呼叫 OS 服務GetMBNoDefault
( source )。在執行問題中指定的轉換時檢查 SQL Server 呼叫堆棧可以確認這一點:
從 Unicode 數據轉換為特定程式碼頁採用所謂的“最佳匹配”策略(如@Paul 的回答和@Martin 在對問題的評論中指出的連結中所述)。根據.NET Framework 中字元編碼的MSDN 頁面:
最佳匹配映射是將 Unicode 數據編碼為程式碼頁數據的 Encoding 對象的預設行為…
但是這些映射到底是什麼?該 MSDN 頁面用於說明以下內容:
最佳匹配策略因不同的程式碼頁而異,並且沒有詳細記錄。
然而,這並不完全正確。也許沒有準確記錄確定映射的“策略”。好的。但是,映射本身是記錄在案的,只是不是在最容易找到的地方。
因此,感謝 Microsoft 將文件移至 GitHub,該頁面現在聲明如下(因為我更新了它😸):
最佳擬合策略沒有詳細記錄。但是, Unicode Consortium 的網站上記錄了幾個程式碼頁。請查看該文件夾中的readme.txt文件,了解如何解釋映射文件。
如果您轉到以下 URL,您將看到幾個文件的列表,每個文件都以將 Unicode 字元映射到的程式碼頁命名:
<ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/>
大多數文件最後一次更新(或至少放置在那裡)是在 2006 年 10 月 4 日,其中一個文件是在 2012 年 3 月 14 日更新的。這些文件的第一部分將 ASCII 程式碼映射到等效的 Unicode 程式碼點。但是每個文件的第二部分將 Unicode 字元映射到它們的 ASCII“等價物”。
我編寫了一個測試腳本,它使用 Code Page 1252 映射來檢查 SQL Server 是否真的在使用這些映射。這可以通過回答以下兩個問題來確定:
- 對於所有映射的程式碼點,SQL Server 是否將它們轉換為指定的映射?
- 對於所有未映射的程式碼點,SQL Server 是否將它們中的任何一個轉換為非“
?
”字元?測試腳本太長放在這裡,所以我把它貼在 Pastebin 上:
SQL Server 中的 Unicode 到程式碼頁的映射
執行腳本將顯示上面第一個問題的答案是“是”(意味著所有提供的映射都得到遵守)。它還將顯示第二個問題的答案是“否”(意思是,沒有任何未映射的程式碼點轉換為“未知”字元之外的任何內容)。因此,該映射文件非常準確:-)。