我对西里尔字符“E”和“e”有疑问,当使用不区分大小写的排序规则时,它们应该相等。这对于除哈萨克语之外的所有归类都是正确的。
我使用以下查询检查了所有 CI 排序规则:
SELECT 'DECLARE @Test_' + name + ' TABLE (
Code nvarchar(32) COLLATE ' + name + '
)
INSERT @Test_' + name + ' (Code)
VALUES (N''Е''), (N''е'')
SELECT ''' + name + ''', * FROM @Test_' + name + ' WHERE Code = N''Е'' COLLATE ' + name
FROM sys.fn_helpcollations()
WHERE name LIKE '%CI%'
对于所有排序规则,但不是哈萨克语,都会按预期返回两行。
用于说明问题的示例查询:
SET NOCOUNT ON;
DECLARE @Test TABLE (
Code nvarchar(32) COLLATE Kazakh_90_CI_AS
);
DECLARE @UpperChar nchar(1) = N'Е';
DECLARE @LowerChar nchar(1) = N'е';
SELECT ASCII(@UpperChar) AS 'UpperChar ASCII', ASCII(@LowerChar) AS 'LowerChar ASCII';
/* Just ASCII-codes for the chars
UpperChar ASCII LowerChar ASCII
--------------- ---------------
197 229
*/
INSERT @Test (Code)
VALUES (@UpperChar), (@LowerChar);
SELECT DISTINCT Code AS 'DISTINCT Code' FROM @Test;
/* Should be one row with CI collation - FALSE
DISTINCT Code
--------------------------------
Е
е
*/
SELECT Code AS 'Code = @UpperChar'
FROM @Test
WHERE Code = @UpperChar;
/* Should be two rows with CI collation - FALSE
Code = @UpperChar
--------------------------------
Е
*/
SELECT Code AS 'Code = @LowerChar'
FROM @Test
WHERE Code = @LowerChar;
/* Should be two rows with CI collation - FALSE
Code = @LowerChar
--------------------------------
е
*/
SELECT Code AS 'Code = @UpperChar OR Code = LOWER(@UpperChar)'
FROM @Test
WHERE Code = @UpperChar
OR Code = LOWER(@UpperChar);
/*Check LOWER('Е') = 'е' - TRUE
Code = @UpperChar OR Code = LOWER(@UpperChar)
---------------------------------------------
Е
е
*/
SELECT Code AS 'Code = @LowerChar OR Code = UPPER(@LowerChar)'
FROM @Test
WHERE Code = @LowerChar
OR Code = UPPER(@LowerChar);
/*Check UPPER('е') = 'Е' - TRUE
Code = @LowerChar OR Code = UPPER(@LowerChar)
---------------------------------------------
Е
е
*/
其他 Сyrillic 字符的行为与预期一致。
我可以做什么来解决这个问题?
在我们进入细节之前,这里有两件事通常会有所帮助:
ASCII()
函数用于VARCHAR
数据并且对与数据排序规则关联的代码页敏感(对于变量,它是当前数据库的默认排序规则)。但在这种情况下,我们只处理 Unicode /NVARCHAR
数据,因此UNICODE()
应该改用函数。CHAR()
当处理 ASCII 值/代码点大于 127 的字符时,让脚本使用或函数创建特定字符会很有帮助NCHAR()
。这使得脚本更易于传输,因为在将脚本打开/粘贴到不支持某些字符的环境中时不会出现字符转换问题。而且,它使脚本更具可读性/可理解性,因为在处理看起来像其他角色但实际上不同的角色(例如我们在这里处理的角色)时,读者不会感到困惑。现在,先介绍一些背景知识,这样解释才有意义:
Unicode 排序/比较是通过为每个字符分配多个权重来完成的。两个权重类别是大小写和变音符号(即重音)。拥有多个类别可以更轻松地处理区分大小写与不区分大小写以及区分重音与不区分大小写的各种组合。大多数(如果不是全部)定义的字符都有默认的排序权重。当使用特定的文化/区域设置时,这些默认权重可以被特定于文化的值覆盖。使用美国英语时,将使用默认值(即无覆盖)。这就是为什么其他语言的字符即使在使用归类时仍然经常正确排序(或大部分正确) ,以及为什么美国英语在使用(或等)归类
Latin1_General
时仍能正确工作(因为Hebrew
Japanese
Hebrew
排序规则不会覆盖美国英语字符的权重)。每个字符的各种权重都保存在一个文件中。大写和小写映射位于单独的文件中。而且,虽然 Unicode Consortium 已进入每年更新的节奏,但 Microsoft 的更新频率要低得多。根据他们公开可用的排序权重文件,他们只有以下版本(适用于 Windows):
SQL Server 的版本较少:
_90_
在 SQL Server 2005 中引入了名称中带有的排序规则_100_
SQL Server 2008 中引入了名称中带有的排序规则_140_
在 SQL Server 2017 中引入了名称中带有的排序规则(遗憾的是,此版本的唯一排序规则是日文排序规则)最后,请记住 SQL Server 排序规则基于Windows 排序规则,但并不完全相同。我认为版本 100 排序规则与“Windows Server 2008”文件关联,而版本 80 和 90 排序规则应该与“Windows NT 4.0 到 Windows Server 2003”文件关联最密切(自 Vista 于 2007 年发布以来) .
考虑到所有这些:
只是为了让任何阅读本文的人都不会感到困惑:这里有问题的字符是“西里尔大写字母 Ie:Е”和“西里尔小写字母 Ie:е”(分别是 Unicode 代码点 U+0415 和 U+0435),看起来与拉丁字符“E”和“e”相同,但绝对不同。例如:
回报:
所有 Microsoft 排序权重文件的默认行为是这两个字符除了大小写外都是相同的。这就是为什么它们在除排序规则之外的所有不区分大小写的排序规则中比较相等的原因
Kazakh
。为什么这两个字符——U+0415 和 U+0435——在使用不区分大小写的哈萨克语归类时比较不同?因为(无论出于何种原因),在使用哈萨克语时,“Windows NT 4.0 到 Windows Server 2003”和“Windows Server 2008”排序权重文件都包含对 U+0435(“西里尔小写字母 Ie:е”)的覆盖文化。覆盖使此字符 U+0435 等同于以下字符(以及其他一些字符):
哈萨克语特定的覆盖还导致字符“西里尔小写字母 Io”(U+0451) 不再等同于其大写字符:
所有这些行为都是 Microsoft 最初实施 Unicode 的残余(回到 Windows NT 4.0!)。值得赞扬的是,Microsoft 是 Unicode 的早期采用者,这种行为可能就像它在 Unicode 1.0 版中的定义一样。很难确定,因为要找到 Unicode 的原始整理文件并不容易(我认为 2.1 版是我能找到的最早版本)。但是,我可以说,通过查看 Microsoft 提供的文件,即使他们更新了默认排序权重并在每个文件中添加了字符和文化,一些定义(例如哈萨克语特定的覆盖)直到最近才更新. 事实上,直到“Windows 8 和 Windows Server 2012”文件(即第二组最近的定义),他们才修复哈萨克语特定的覆盖(我假设其他)。
因此,即使 Windows 通常(也可能还有 .NET)正确处理哈萨克语排序规则(从 Windows 8 和 Windows Server 2012 开始),SQL Server 排序规则仍然停留在过去。意思是,微软将这些归类更新为更新版本的 Unicode 是一个奇迹,没有解决办法。我确实有一个想法,我一直在考虑向他们提议如何使它比简单的定义更新更好,所以也许我应该继续提交(我会在完成后用链接更新这个答案那)。
但是现在,如果你真的需要这两个字符在不区分大小写的哈萨克语排序规则中相等(并且不要忘记“西里尔小写字母 Io”(U+0451)),你将不得不从你的例子中做到这
UPPER()
一点(虽然我不能肯定地说这种解决方法不会引起任何问题)。有关排序规则、Unicode 和编码的更多信息,请访问我的网站:Collations.Info