一个“字符”(可以由多个代码点组成:代理对,组合字符等)与另一个的比较是基于一组相当复杂的规则。由于需要考虑Unicode规范中表示的所有语言中的所有各种规则(有时是“古怪的”)规则,因此它是如此复杂。此系统适用于所有NVARCHAR
数据的非二进制排序规则,以及VARCHAR
使用Windows排序规则而不是SQL Server排序规则(以开头的数据SQL_
)的数据。该系统不适用于VARCHAR
使用SQL Server排序规则的数据,因为那些使用简单映射。
大多数规则在Unicode排序算法(UCA)中定义。其中一些规则,以及一些未在UCA中涵盖的规则是:
allkeys.txt
文件中给出的默认订购/重量(如下所示)
- 使用哪种敏感度和选项(例如,是区分大小写还是不区分大小写?如果敏感,那么是大写优先还是小写优先?)
- 任何基于语言环境的替代。
- 使用的是Unicode标准版本。
- “人为”因素(即Unicode是一种规范,而不是软件,因此由每个供应商来实施)
我强调了有关人为因素的最后一点,希望可以清楚地表明,不应指望SQL Server根据规范始终表现为100%。
这里的首要因素是赋予每个代码点的权重,以及多个代码点可以共享相同的权重规范这一事实。您可以在此处找到基本权重(没有特定于语言环境的替代)(我相信“ 100
归类” 系列是Unicode v 5.0-在Microsoft Connect项目的注释中进行的非正式确认):
http://www.unicode.org/Public/UCA/5.0.0/allkeys.txt
有问题的代码点– U + FFFD –定义为:
FFFD ; [*0F12.0020.0002.FFFD] # REPLACEMENT CHARACTER
该符号在UCA的9.1 Allkeys文件格式部分中定义:
<entry> := <charList> ';' <collElement>+ <eol>
<charList> := <char>+
<collElement> := "[" <alt> <weight> "." <weight> "." <weight> ("." <weight>)? "]"
<alt> := "*" | "."
Collation elements marked with a "*" are variable.
最后一行很重要,因为我们正在查看的代码点具有确实以“ *”开头的规范。在第3.6节“ 可变权重”中,根据我们无法直接访问的归类配置值定义了四种可能的行为(这些行为被硬编码到每个归类的Microsoft实现中,例如区分大小写是先使用小写字母还是使用小写字母首先是大写,这是一个属性,该属性在VARCHAR
使用SQL_
排序规则和所有其他变体的数据之间是不同的。
我没有时间对采用哪种路径进行全面研究,也没有时间推断使用了哪些选项,以便可以给出更可靠的证据,但是可以肯定地说,在每个代码点规范中,无论是否被认为“相等”并不总是使用完整的规范。在这种情况下,我们具有“ 0F12.0020.0002.FFFD”,并且很可能仅使用了第2级和第3级(即.0020.0002。)。在记事本++中为“ .0020.0002”执行“计数”。找到12,581个匹配项(包括我们尚未处理的辅助字符)。对“ [*”进行“计数”将返回4049个匹配项。使用以下模式进行RegEx“查找” /“计数”\[\*\d{4}\.0020\.0002
返回832个匹配项。因此,在此组合中的某处,可能还有一些我未看到的其他规则,再加上一些特定于Microsoft的实现详细信息,充分说明了此行为。而且要明确的是,所有匹配字符的行为都是相同的,因为它们相互匹配,因为一旦应用规则,它们的权重都相同(意味着,可能有人问过这个问题,不是一定是先生�
)。
您可以在下面的查询中看到,并根据查询下面COLLATE
的结果更改子句,两种敏感性在Collations的两个版本中如何起作用:
;WITH cte AS
(
SELECT TOP (65536) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS [Num]
FROM [master].sys.columns col
CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
CONVERT(VARBINARY(2), cte.Num) AS [Hex],
NCHAR(cte.Num) AS [Character]
FROM cte
WHERE NCHAR(cte.Num) = NCHAR(0xFFFD) COLLATE Latin1_General_100_CS_AS_WS --N'�'
ORDER BY cte.Num;
下面是在不同归类中匹配字符的各种计数。
Latin1_General_100_CS_AS_WS = 5840
Latin1_General_100_CS_AS = 5841 (The "extra" character is U+3000)
Latin1_General_100_CI_AS = 5841
Latin1_General_100_CI_AI = 6311
Latin1_General_CS_AS_WS = 21,229
Latin1_General_CS_AS = 21,230
Latin1_General_CI_AS = 21,230
Latin1_General_CI_AI = 21,537
在上面列出的所有归类中,N'' = N'�'
也将评估为true。
更新
我能够进行更多研究,这是我发现的内容:
它“可能”如何工作
使用ICU Collation Demo,我将语言环境设置为“ en-US-u-va-posix”,将强度设置为“ primary”,选中显示“ sort keys”,并粘贴了以下4个字符,这些字符是我从上面查询的结果(使用Latin1_General_100_CI_AI
归类):
�
Ԩ
ԩ
Ԫ
然后返回:
Ԫ
60 2E 02 .
Ԩ
60 7A .
ԩ
60 7A .
�
FF FD .
然后,在http://unicode.org/cldr/utility/character.jsp?a=fffd中检查字符属性“。”,并确认1级排序键(即FF FD
)与“ uca”属性匹配。单击该“ uca”属性将带您到搜索页面– http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D –仅显示1个匹配项。并且,在allkeys.txt文件中,级别1的排序权重显示为0F12
,并且只有1个匹配项。
为了确保我们正确解释的行为,我看着另一个角色:希腊大写字母OMICRON与VARIA Ὸ
在http://unicode.org/cldr/utility/character.jsp?a=1FF8具有“UCA”(即第1级排序权重/整理元素)5F30
。单击该“ 5F30”会将我们带到搜索页面– http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3D5F30%3A%5D –显示30个匹配项,其中20个匹配项它们在0-65535范围内(即U + 0000-U + FFFF)。在allkeys.txt文件中查找代码点1FF8,我们看到1级排序权重为12E0
。在记事本上执行“计数”12E0.
显示30个匹配项(这与Unicode.org的结果匹配,但由于文件用于Unicode v 5.0,并且网站使用的是Unicode v 9.0数据,因此不能保证此结果)。
在SQL Server中,以下查询返回20个匹配项,与删除10个补充字符时的Unicode.org搜索相同:
;WITH cte AS
(
SELECT TOP (65535) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [Num]
FROM [master].sys.columns col
CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
CONVERT(VARCHAR(50), CONVERT(VARBINARY(2), cte.Num), 2) AS [Hex],
NCHAR(cte.Num) AS [Character]
FROM cte
WHERE NCHAR(cte.Num) = NCHAR(0x1FF8) COLLATE Latin1_General_100_CI_AI
ORDER BY cte.Num;
而且,可以肯定的是,返回到ICU排序规则演示页,并使用SQL Server的20个结果列表中的以下3个字符替换“输入”框中的字符:
Ὂ
𝜪
Ὸ
显示它们确实具有相同的5F 30
1级排序权重(与角色属性页面上的“ uca”字段匹配)。
SO,它的确看起来好像这个特定字符应该不匹配任何东西。
实际工作方式(至少在Microsoft-land中)
与SQL Server内部不同,.NET可以通过CompareInfo.GetSortKey方法显示字符串的排序键。使用此方法并仅传入U + FFFD字符,它将返回的排序键0x0101010100
。然后,遍历0到65535范围内的所有字符,以查看哪些字符具有0x0101010100
返回4529个匹配项的排序键。这与SQL Server(使用Latin1_General_100_CS_AS_WS
归类时)返回的5840不完全匹配,但是鉴于我正在运行Windows 10和使用Unicode v的.NET Framework 4.6.1版,这是我们(现在)可以获得的最接近的5840。6.3.0根据CharUnicodeInfo类的图表(在“备注”中的“给呼叫者的注释”中)。目前,我正在使用SQLCLR函数,因此无法更改目标Framework版本。如果有机会,我将创建一个控制台应用程序,并使用4.5的目标Framework版本,因为该版本使用的是Unicode v 5.0,该版本应与100系列排序规则匹配。
该测试表明,即使在.NET和U + FFFD的SQL Server之间没有完全相同的匹配次数,也很清楚这不是 SQL Server特定的行为,是否对实现进行了有意或无意的检查由Microsoft提供的U + FFFD字符确实确实匹配了很多字符,即使它不符合Unicode规范。并且,鉴于此字符匹配U + 0000(空),这可能只是缺少权重的问题。
也
关于=
查询与LIKE N'%�%'
查询在行为上的差异,这与通配符和这些(即� Ƕ Ƿ Ǹ
)字符缺少的权重(我假设)有关。如果将LIKE
条件更改为简单条件,LIKE N'�'
则它将返回与=
条件相同的3行。如果通配符的问题不是由于权重“丢失”(没有0x00
由CompareInfo.GetSortKey
btw 返回的排序键),则可能是由于这些字符可能具有允许排序键根据上下文而变化的属性(即,周围的字符) )。
FFFD
(搜索*0F12.0020.0002.FFFD
仅返回一个结果)。从@Forrest的观察来看,它们都与空字符串匹配,并且在主题上的阅读更多,因此我认为它们在各种非二进制排序规则中所占的权重实际上为零。