Mar*_*ith 26 sql-server like unicode sql-server-2016
DECLARE @T TABLE(
Col NCHAR(1));
INSERT INTO @T
VALUES (N'A'),
(N'B'),
(N'C'),
(N'?'),
(N'?'),
(N'?');
Run Code Online (Sandbox Code Playgroud)
SELECT *
FROM @T
WHERE Col LIKE N'%?%'
Run Code Online (Sandbox Code Playgroud)
Col
A
B
C
?
?
?
Run Code Online (Sandbox Code Playgroud)
SELECT *
FROM @T
WHERE Col = N'?'
Run Code Online (Sandbox Code Playgroud)
退货
Col
?
?
?
Run Code Online (Sandbox Code Playgroud)
使用下面的生成每个可能的双字节“字符”显示=
版本匹配其中的 21,229 个和LIKE N'%?%'
所有版本(我尝试了一些非二进制排序规则,结果相同)。
WITH T(I, N)
AS
(
SELECT TOP 65536 ROW_NUMBER() OVER (ORDER BY @@SPID),
NCHAR(ROW_NUMBER() OVER (ORDER BY @@SPID))
FROM master..spt_values v1,
master..spt_values v2
)
SELECT I, N
FROM T
WHERE N = N'?'
Run Code Online (Sandbox Code Playgroud)
任何人都能够阐明这里发生了什么?
使用COLLATE Latin1_General_BIN
then 匹配单个字符NCHAR(65533)
- 但问题是要了解它在其他情况下使用的规则。这 21,229 个与 匹配的字符有什么特别之处=
,为什么所有内容都与通配符匹配?我想我失踪的背后有某种原因。
nchar(65534)
[和其他 21,000] 工作与nchar(65533)
. 这个问题可以用nchar(502)
同样的方式来表达?
- 它的行为与LIKE N'%?%'
(匹配所有内容)和=
案例中的行为相同。这大概是一个很大的线索。
将SELECT
最后一个查询中的更改为SELECT I, N, RANK() OVER(ORDER BY N)
表明 SQL Server 无法对字符进行排名。似乎没有由排序规则处理的任何字符都被认为是等效的。
具有Latin1_General_100_CS_AS
排序规则的数据库产生 5840 个匹配项。大大Latin1_General_100_CS_AS
减少了=
比赛,但不会改变LIKE
行为。看起来确实有一堆字符在后来的排序规则中变小了,这些字符都比较相等,LIKE
然后在通配符搜索中被忽略。
我使用的是 SQL Server 2016。符号?
是 Unicode 替换字符,但 UCS-2 编码中唯一的无效字符是 55296 - 57343 AFAIK,它显然匹配完全有效的代码点,例如N'?'
不在此范围内的代码点。
这些特点表现得就像一个空字符串LIKE
和=
。他们甚至评估为等效。N'' = N'?'
是真的,你可以把它放在LIKE
单个空格的比较中LIKE '_' + nchar(65533) + '_'
而不会有任何影响。LEN
比较会产生不同的结果,所以它可能只是某些字符串函数。
我认为这种LIKE
行为是正确的;它的行为就像一个未知值(可以是任何值)。这些其他字符也会发生这种情况:
nchar(11217)
(不确定性标志)nchar(65532)
(对象替换字符)nchar(65533)
(替换字符)nchar(65534)
(不是字符)因此,如果我想找到所有用等号表示不确定性的字符,我将使用支持补充字符的排序规则,例如Latin1_General_100_CI_AS_SC
.
我猜这些是文档Collation 和 Unicode Support 中提到的“非加权字符”组。
Sol*_*zky 13
一个“字符”(可以由多个代码点组成:代理对、组合字符等)与另一个“字符”的比较基于一组相当复杂的规则。由于需要考虑Unicode规范中表示的所有语言中的所有各种(有时是“古怪的”)规则,因此它非常复杂。此系统适用于所有NVARCHAR
数据的非二进制排序规则,以及VARCHAR
使用 Windows 排序规则而非 SQL Server 排序规则(以 开头的数据SQL_
)。该系统不适用于VARCHAR
使用 SQL Server 排序规则的数据,因为这些数据使用简单映射。
大多数规则在Unicode Collation Algorithm (UCA)中定义。其中一些规则以及 UCA 未涵盖的一些规则是:
allkeys.txt
文件中给出的默认排序/重量(如下所述)我强调了关于人为因素的最后一点,希望明确指出不应期望 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
Run Code Online (Sandbox Code Playgroud)
该符号在 UCA 的第9.1节Allkeys 文件格式中定义:
<entry> := <charList> ';' <collElement>+ <eol>
<charList> := <char>+
<collElement> := "[" <alt> <weight> "." <weight> "." <weight> ("." <weight>)? "]"
<alt> := "*" | "."
Collation elements marked with a "*" are variable.
Run Code Online (Sandbox Code Playgroud)
最后一行很重要,因为我们正在查看的代码点有一个确实以“*”开头的规范。在第3.6节变量权重中,根据我们无法直接访问的排序规则配置值定义了四种可能的行为(这些被硬编码到每个排序规则的 Microsoft 实现中,例如区分大小写是先使用小写还是先使用小写?大写优先,VARCHAR
使用SQL_
排序规则和所有其他变体的数据之间不同的属性)。
我没有时间对采用哪些路径进行全面研究,也没有时间推断正在使用哪些选项,以便提供更可靠的证据,但可以肯定地说,在每个代码点规范中,无论是否有某些东西被认为“相等”并不总是使用完整的规范。在这种情况下,我们有“0F12.0020.0002.FFFD”,很可能只是使用了级别 2 和级别 3(即.0020.0002.)。在 Notepad++ 中为“.0020.0002”做“计数”。找到 12,581 个匹配项(包括我们尚未处理的补充字符)。对“[*”进行“计数”会返回 4049 个匹配项。使用以下模式进行正则表达式“查找”/“计数”\[\*\d{4}\.0020\.0002
返回 832 个匹配项。所以在这个组合中的某个地方,再加上我没有看到的其他一些规则,再加上一些 Microsoft 特定的实现细节,就是对这种行为的完整解释。需要明确的是,所有匹配字符的行为都是相同的,因为它们都相互匹配,因为一旦应用规则,它们都具有相同的权重(意思是,这个问题可能是针对其中任何一个而提出的,而不是必然先生?
)。
您可以查看下面的查询,并根据查询下方COLLATE
的结果更改子句,不同的敏感性如何跨两个版本的排序规则工作:
;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;
Run Code Online (Sandbox Code Playgroud)
不同排序规则中匹配字符的各种计数如下。
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
Run Code Online (Sandbox Code Playgroud)
在上面列出的所有排序规则中,N'' = N'?'
也评估为 true。
我能够做更多的研究,这是我发现的:
它“可能”应该如何工作
使用ICU Collation Demo,我将语言环境设置为“en-US-u-va-posix”,将强度设置为“primary”,检查显示“排序键”,并粘贴了我从上面查询的结果(使用Latin1_General_100_CI_AI
排序规则):
?
?
?
?
Run Code Online (Sandbox Code Playgroud)
然后返回:
?
60 2E 02 .
?
60 7A .
?
60 7A .
?
FF FD .
Run Code Online (Sandbox Code Playgroud)
然后,检查“?”的字符属性。在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)。查看代码点1FF8的allkeys.txt文件,我们看到 1 级排序权重为。在 Notepad++ 中进行“计数”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;
Run Code Online (Sandbox Code Playgroud)
并且,为了确定,返回 ICU Collation Demo 页面,并将“输入”框中的字符替换为以下 3 个字符,这些字符取自 SQL Server 的 20 个结果列表:
?
?
Run Code Online (Sandbox Code Playgroud)
表明它们确实都具有相同的5F 30
1 级排序权重(与字符属性页面上的“uca”字段匹配)。
SO,它的确看起来好像这个特定字符应该不匹配任何东西。
它是如何工作的(至少在微软领域)
与 SQL Server 不同,.NET 有一种方法可以通过CompareInfo.GetSortKey Method显示字符串的排序键。使用此方法并仅传入 U+FFFD 字符,它会返回排序键0x0101010100
. 然后,遍历 0 - 65535 范围内的所有字符以查看哪些字符具有0x0101010100
返回 4529 个匹配项的排序键。这与 SQL Server 中返回的 5840 不完全匹配(使用Latin1_General_100_CS_AS_WS
排序规则时),但鉴于我正在运行 Windows 10 和 .NET Framework 4.6.1 版(使用 Unicode v),这是我们可以得到的(目前)最接近的6.3.0 根据CharUnicodeInfo 类的图表(在“来电者须知”,“备注”部分)。目前我正在使用 SQLCLR 函数,因此无法更改目标框架版本。当我有机会时,我将创建一个控制台应用程序并使用 4.5 的目标框架版本,因为它使用 Unicode v 5.0,它应该与 100 系列排序规则相匹配。
这个测试表明,即使 .NET 和 SQL Server 之间没有完全相同数量的 U+FFFD 匹配,很明显这不是SQL Server 特定的行为,无论是有意的还是在实施过程中的疏忽微软表示,U+FFFD 字符确实匹配了相当多的字符,即使它不应该根据 Unicode 规范。而且,鉴于此字符与 U+0000 (null) 匹配,这可能只是缺少权重的问题。
还
关于=
查询与LIKE N'%?%'
查询中行为的差异,它与通配符和这些(即? ? ? ?
)字符的缺失(我假设)权重有关。如果LIKE
条件更改为简单,LIKE N'?'
则它返回与=
条件相同的 3 行。如果通配符的问题不是由于“丢失”权重(顺便说一句,没有0x00
返回排序键CompareInfo.GetSortKey
),那么这可能是由于这些字符可能具有允许排序键根据上下文(即周围字符)而变化的属性)。