Sql-Server

為什麼當 Unicode 字元串不為空時,MS SQL Server 會返回空字元串檢查的結果

  • September 20, 2018
select * from (select N'ግዜ ' as t) as t2 where t= ''

字元串 ‘ግዜ ’ 匹配上面的檢查,這是為什麼呢?

雖然我不確定這些特定字元的確切原因,但問題與較舊的排序規則有關(請參閱最後的更新部分)。它們不僅等同於空字元串,還等同於其中一個字元:

SELECT * FROM (SELECT N'ግዜ') tab(col) WHERE tab.col = N'ግ';

如果您嘗試區分大小寫的排序規則,即使有多個字元,它們仍然等同於:

SELECT * FROM (SELECT N'ግዜ') t(c) WHERE t.c = N'ግግግግ' COLLATE SQL_Latin1_General_CP1_CS_AS;
SELECT * FROM (SELECT N'ግ') t(c) WHERE t.c = N'ዜዜዜዜ' COLLATE SQL_Latin1_General_CP1_CS_AS;
SELECT * FROM (SELECT N'ዜ') t(c) WHERE t.c = N'ግግግግ' COLLATE SQL_Latin1_General_CP1_CS_AS;

即使是“等效”的 Windows 排序規則也有同樣的問題:

SELECT * FROM (SELECT N'ግዜ') t(c) WHERE t.c = N'ግ' COLLATE Latin1_General_CS_AS;
SELECT * FROM (SELECT N'ግዜ') t(c) WHERE t.c = N'ግ' COLLATE Latin1_General_CS_AS_KS_WS;

但是,似乎較新版本的 Windows 排序規則(即 100 系列或更新版本)“修復”了該問題,這些不再等同於:

SELECT * FROM (SELECT N'ግዜ') t(c) WHERE t.c = N'ግ' COLLATE Latin1_General_100_CI_AI;
SELECT * FROM (SELECT N'ግዜ') t(c) WHERE t.c = N'ግ' COLLATE Latin1_General_100_CI_AS;

而且,當然,二進制 Windows 排序規則(舊系列和新系列)工作得很好,因為以下不報告匹配:

SELECT * FROM (SELECT N'ግዜ') t(c) WHERE t.c = N'ግ' COLLATE Latin1_General_BIN;
SELECT * FROM (SELECT N'ግዜ') t(c) WHERE t.c = N'ግ' COLLATE Latin1_General_BIN2;
SELECT * FROM (SELECT N'ግዜ') t(c) WHERE t.c = N'ግ' COLLATE Latin1_General_100_BIN;
SELECT * FROM (SELECT N'ግዜ') t(c) WHERE t.c = N'ግ' COLLATE Latin1_General_100_BIN2;

更新 (2015-08-20)在瀏覽http://www.unicode.org/、http://site.icu-project.org/和其他幾個 Unicode 相關網站

的文件 6 小時後,我放棄了尋找可能在 2008 年之前發生的“加權”變化的證據(SQL Server 2008 中引入了新的 100 系列排序規則)。但是,我確實在www.fileformat.info找到了以下關於此處測試的兩個字元的資訊:

因此,我繼續進行下一個項目,不久之後在 SQL Server 2008 MSDN 頁面上發現了以下關於排序和 Unicode 支持的內容:

SQL Server 2008 引入了與 Windows Server 2008 提供的排序規則完全一致的新排序規則。這 80 個新排序規則由 *_100 版本引用表示。它們為使用者提供最新且語言準確的文化分類約定。支持包括以下內容:

  • 權重已添加到以前可以平等比較的非加權字元。

一個字元沒有排序權重意味著它實際上是不可見的。

故事的寓意:不要那麼努力;早點放棄;-)

更新(2018-09-20)

為了更直覺地了解正在發生的事情,下面的查詢將每個 BMP 字元(程式碼點 0 - 65535 / U+0000 - U+FFFF)與空字元串進行比較。使用不同的排序規則重複比較:BIN2,一個 SQL Server 排序規則,從 SQL Server 2000 開始的 Latin1_General,從 SQL Server 2008 開始的 Latin1_General,從 SQL Server 2008 開始的 Japanese_XJIS,以及從 SQL Server 2017 開始的 Japanese_XJIS。這兩個從 SQL Server 2008 開始的排序規則顯示兩者都返回相同數量的匹配項,但較新的 Japanese_XJIS 排序規則返回不同的數字(SQL Server 2017 中唯一更新的排序規則是日語排序規則)。這樣做是為了顯示在各種排序規則版本中缺少多少字元的排序權重。

;WITH nums AS
(
 SELECT TOP (65536) (ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1) AS [CodePoint]
 FROM   [master].[sys].[columns] col
 CROSS JOIN [master].[sys].[objects] obj
)
SELECT nums.[CodePoint],
      COALESCE(NCHAR(nums.[CodePoint]), N'TOTALS:') AS [Character],
      COUNT(CASE WHEN (NCHAR(nums.[CodePoint]) = N''
                COLLATE Latin1_General_BIN2) THEN 1 END) AS [BIN2],
      COUNT(CASE WHEN (NCHAR(nums.[CodePoint]) = N''
                COLLATE SQL_Latin1_General_CP1_CS_AS) THEN 1 END) AS [SQL Collations],
      COUNT(CASE WHEN (NCHAR(nums.[CodePoint]) = N''
                COLLATE Latin1_General_CS_AS_KS_WS) THEN 1 END) AS [SQL2000 Latin1],
      COUNT(CASE WHEN (NCHAR(nums.[CodePoint]) = N''
                COLLATE Latin1_General_100_CS_AS_KS_WS) THEN 1 END) AS [SQL2008 Latin1],
      COUNT(CASE WHEN (NCHAR(nums.[CodePoint]) = N''
                COLLATE Japanese_XJIS_100_CS_AS_KS_WS) THEN 1 END) AS [SQL2008 Japanese],
      COUNT(CASE WHEN (NCHAR(nums.[CodePoint]) = N''
                COLLATE Japanese_XJIS_140_CS_AS_KS_WS) THEN 1 END) AS [SQL2017 Japanese]
FROM   nums
GROUP BY ROLLUP ((nums.[CodePoint], NCHAR(nums.[CodePoint])));

要查看所有行的詳細資訊,請執行上面的查詢。但只是為了總結,那就是:

BIN2  SQL Collations SQL2000 Latin1  SQL2008 Latin1  SQL2008 Japanese  SQL2017 Japanese
1     21230          21229           5840            5840              3375

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