2015-05-22 32 views
4

我遇到了一個情況下SQL服務器可以存儲「索菲亞」和「索菲亞」是兩個不同的字符串,但在TSQL比較時,他們是不管逐份使用,即使二進制整理相同:爲什麼TSQL將「sofia」視爲「sofia」?這是什麼字符串編碼?

CREATE TABLE #R (NAME NvarchAR(255) COLLATE SQL_Latin1_General_CP1_CI_AS) 
INSERT INTO #R VALUES (N'sofia') 
INSERT INTO #r VALUES (N'sofia') 

SELECT * FROM #r WHERE NAME = N'sofia' 

sofia 
sofia 

(2 row(s) affected) 

IF 'sofia' = 'sofia' COLLATE SQL_Latin1_General_CP1_CI_AS 
SELECT 'Values are the same' 
ELSE 
SELECT 'Values are different' 

------------------- 
Values are the same 

(1 row(s) affected) 

IF 'sofia' = 'sofia' COLLATE SQL_Latin1_General_CP437_BIN 
SELECT 'Values are the same' 
ELSE 
SELECT 'Values are different' 

------------------- 
Values are the same 

(1 row(s) affected) 

I tried to find out the encode of "sofia" 

http://stackoverflow.com/questions/1025332/determine-a-strings-encoding-in-c-sharp 

It said: 

      // If all else fails, the encoding is probably (though certainly not 
      // definitely) the user's local codepage! One might present to the user a 
      // list of alternative encodings as shown here: http://stackoverflow.com/questions/8509339/what-is-the-most-common-encoding-of-each-language 
      // A full list can be found using Encoding.GetEncodings(); 

I iterate through all the encoding returned from Encoding.GetEncodings(), none of them match 

Looking into the binary I found an interesting fact: 「sofia」 itself is encoded with UTF16, but it can be generated from "SOFIA" UTF16 by filling 「1」 instead of 「0」 in the extra byte besides ASCII code (Ex for ‘S’: 83 255 vs 83 0) It is shown as lower case. In C#, 

「sofia」 

          [0]   83   byte          
          [1]   255  byte 
          [2]   79   byte 
          [3]   255  byte 
          [4]   70   byte 
          [5]   255  byte 
          [6]   73   byte 
          [7]   255  byte 
          [8]   65   byte 
          [9]   255  byte 

"SOFIA" 

          [0]   83   byte          
          [1]   0  byte 
          [2]   79   byte 
          [3]   0  byte 
          [4]   70   byte 
          [5]   0  byte 
          [6]   73   byte 
          [7]   0  byte 
          [8]   65   byte 
          [9]   0  byte 

"sofia" 

          [0]   115   byte          
          [1]   0  byte 
          [2]   79   byte 
          [3]   0  byte 
          [4]   70   byte 
          [5]   0  byte 
          [6]   105   byte 
          [7]   0  byte 
          [8]   97   byte 
          [9]   0  byte 

One can create two different directorie/files with name as C:\sofia\, C:\sofia\ or sofia.txt, sofia.txt. 

Why does the SQL engine think they are the same while storing them with the original streams? 

In order to get just the exact I want I had to convert to binary first: 

SELECT * FROM #r WHERE CONVERT(VARBINARY(100), Name) = CONVERT(VARBINARY(100), N'sofia') 

sofia 

(1 row(s) affected) 

SELECT * FROM #r WHERE CONVERT(VARBINARY(100), Name) = CONVERT(VARBINARY(100), N'sofia') 

sofia 

(1 row(s) affected) 

但這有很多副作用,如文化和案例。我如何教導 TSQL引擎知道他們是不同的,沒有太多的成本?

是否有這種字符串編碼的正式名稱?

+0

我很好奇,如果我的回答幫助你解決了你的問題。 –

回答

2

我相信你要找的是半角寬和全角字符的區別。根據您的表格使用的排序規則,這些將被視爲相同或不同。在這種情況下,您使用的顯然是寬度不敏感的SQL_Latin1_General_CP1_CI_AS

可以通過附加_WSaccording to this,所以改變歸類到SQL_Latin1_General_CP1_CI_AS_WS應該將它們視爲不相等添加寬度靈敏度。

編輯:正如@srutzky指出的,而不是僅僅將_WS添加到排序規則中,您將需要在其中找到一個_WS。

+1

僅供參考:您不能簡單地將'_WS'添加到任何排序規則名稱並假定它將是有效的排序規則。實際上,'SQL_Latin1_General_CP1_CI_AS_WS'不是有效的排序規則。嘗試一下,看看它是否有效。 –

+0

有趣,謝謝澄清。 – Kazetsukai

6

這裏有兩個問題。

第一個:存在排序問題。排序規則定義了字符排序和等同性。正如@Kazetsukai所建議的那樣,這裏提供的具體排序屬性是寬度敏感。但是,不能簡單地將_WS添加到任何排序規則名稱,並假定它將是有效的排序規則。而實際上,SQL_Latin1_General_CP1_CI_AS_WS不是有效的排序規則。

您可以通過SELECT * FROM fn_helpcollations() WHERE [name] LIKE N'latin%[_]ws';獲得一組有限的排序規則。該查詢的結果表明您可能想要的排序規則是Latin1_General_CI_AS_WS。並且任何以_BIN2結尾的排序規則都會起作用(儘量不要使用以_BIN結尾的排序規則,因爲那些已被棄用的排序規則正如SQL_開頭的排序規則一樣)。

但是,出於某種原因,即使使用那些似乎不工作:

IF 'sofia' = 'sofia' COLLATE Latin1_General_CI_AS_WS 
SELECT 'Values are the same' 
ELSE 
SELECT 'Values are different' 

IF 'sofia' = 'sofia' COLLATE Latin1_General_BIN2 
SELECT 'Values are the same' 
ELSE 
SELECT 'Values are different' 

兩個結果都是「值是相同的」。這給我們帶來了:

二:NVARCHAR 數據時,必須前綴字符串常量與資本N,否則它隱含的人物首先將各自VARCHAR 字符(或如果Unicode代碼點與字段或操作的排序規定的代碼頁中存在的字符之間沒有定義映射,則字符將轉換爲?

IF N'sofia' = N'sofia' COLLATE Latin1_General_CI_AS_WS 
SELECT 'Values are the same' 
ELSE 
SELECT 'Values are different' 

IF N'sofia' = N'sofia' COLLATE Latin1_General_BIN2 
SELECT 'Values are the same' 
ELSE 
SELECT 'Values are different' 

,前綴那些文字值與N允許預期的行爲,併爲兩個查詢的結果是現在「值是不同的。」


XMLN -prefixed類型存儲數據爲UTF-16小字節序。默認處理只是UCS-2/Base多語言平面(BMP)字符。但是,如果使用以_SC結尾的歸類,則可以使用補充字符正確處理完整的UTF-16。

CHARVARCHARTEXT(但不使用這最後一個,因爲它是不建議使用)類型與代碼頁擴展的8位ASCII。