Ara*_*ram 19 sql-server collation unicode
为什么这两个SELECT
语句会导致不同的排序顺序?
USE tempdb;
CREATE TABLE dbo.OddSort
(
id INT IDENTITY(1,1) PRIMARY KEY
, col1 NVARCHAR(2)
, col2 NVARCHAR(2)
);
GO
INSERT dbo.OddSort (col1, col2)
VALUES (N'e', N'eA')
, (N'é', N'éB')
, (N'ë', N'ëC')
, (N'è', N'èD')
, (N'ê', N'êE')
, (N'?', N'?F');
GO
SELECT *
FROM dbo.OddSort
ORDER BY col1 COLLATE Latin1_General_100_CS_AS;
Run Code Online (Sandbox Code Playgroud)
???????????????????????? ? ID ?列 1 ? 列 2 ? ???????????????????????? ? 1 ? ? ? ? 2 ? é ? 乙 ? ? 4 ? ? ?D ? -- 应该是id 3? ? 5 ? ? 是吗? ? 3 ? ? ?C ? ? 6 ? ? ? ?F ? ????????????????????????
SELECT *
FROM dbo.OddSort
ORDER BY col2 COLLATE Latin1_General_100_CS_AS;
Run Code Online (Sandbox Code Playgroud)
???????????????????????? ? ID ?列 1 ? 列 2 ? ???????????????????????? ? 1 ? ? ? ? 2 ? é ? 乙 ? ? 3 ? ? ?C ? ? 4 ? ? ?D ? ? 5 ? ? 是吗? ? 6 ? ? ? ?F ? ????????????????????????
Sol*_*zky 16
您在此处看到的行为一般来说是由于Unicode 整理算法 (UCA)允许进行复杂的多级排序。进一步来说:
排序不是比较:
确定两个字符串是相同还是不同是相当简单的(给定特定的区域设置/语言和一组敏感性)。但是确定 2 个或更多字符串的顺序可能非常复杂。
排序是通过一系列步骤完成的,每一步都应用于整个字符串,而不是逐个字符:
当您按col1
(单个字符)排序时,它首先确定所有字符具有相同的权重,因为它们都是“ e ”。接下来,它应用重音/变音符号权重。没有大小写差异,所以第三步不会改变任何东西。所以唯一的区别在于第 2 步,这就是为什么这些行基于col1
.
当您按col2
(两个字符)排序时,它首先确定每一行具有不同的权重,因为这两个字符都用于确定排序权重(例如“ ea ”、“ eb ”等)。接下来,它应用重音/变音符号权重。没有大小写差异,所以第三步不会改变任何东西。所以这次的步骤一和步骤二是有区别的。但是由于在考虑步骤 2 的权重之前,步骤 1 中的差异已经应用于每个字符串,因此步骤 2 的权重对排序没有任何影响;只有当两行或多行的第 1 步的权重相同时,它们才适用。
以下改编自问题的示例代码有望说明上述排序行为。我添加了一些额外的行和一个额外的列,以帮助显示排序规则区分大小写的影响(因为原始样本数据都是小写的):
设置
USE [tempdb];
-- DROP TABLE dbo.OddSort;
CREATE TABLE dbo.OddSort
(
id INT IDENTITY(1,1) PRIMARY KEY,
col1 NVARCHAR(5) COLLATE Latin1_General_100_CS_AS,
col2 NVARCHAR(5) COLLATE Latin1_General_100_CS_AS,
col3 NVARCHAR(5) COLLATE Latin1_General_100_CS_AS
);
GO
INSERT dbo.OddSort (col1, col2, col3)
VALUES (N'e', N'eA', N'e A')
, (N'ê', N'êE', N'ê E')
, (N'é', N'éH', N'é H')
, (N'ë', N'ëC', N'ë C')
, (N'E', N'EG', N'E G')
, (N'Ë', N'ëh', N'ë h')
, (N'è', N'èD', N'è D')
, (N'é', N'éB', N'é B')
, (N'ë', N'ëH', N'ë H')
, (N'?', N'?F', N'? F');
Run Code Online (Sandbox Code Playgroud)
测试 1
SELECT [id], [col1], UNICODE([col1]) AS [CodePoint]
FROM dbo.OddSort
ORDER BY col1;
Run Code Online (Sandbox Code Playgroud)
返回:
USE [tempdb];
-- DROP TABLE dbo.OddSort;
CREATE TABLE dbo.OddSort
(
id INT IDENTITY(1,1) PRIMARY KEY,
col1 NVARCHAR(5) COLLATE Latin1_General_100_CS_AS,
col2 NVARCHAR(5) COLLATE Latin1_General_100_CS_AS,
col3 NVARCHAR(5) COLLATE Latin1_General_100_CS_AS
);
GO
INSERT dbo.OddSort (col1, col2, col3)
VALUES (N'e', N'eA', N'e A')
, (N'ê', N'êE', N'ê E')
, (N'é', N'éH', N'é H')
, (N'ë', N'ëC', N'ë C')
, (N'E', N'EG', N'E G')
, (N'Ë', N'ëh', N'ë h')
, (N'è', N'èD', N'è D')
, (N'é', N'éB', N'é B')
, (N'ë', N'ëH', N'ë H')
, (N'?', N'?F', N'? F');
Run Code Online (Sandbox Code Playgroud)
我们可以在上面的结果中看到:
测试 2
SELECT [id], [col1], UNICODE([col1]) AS [CodePoint]
FROM dbo.OddSort
ORDER BY col1;
Run Code Online (Sandbox Code Playgroud)
返回:
id col1 CodePoint
1 e 101
5 E 69
8 é 233
3 é 233
7 è 232
2 ê 234
4 ë 235
9 ë 235
6 Ë 203
10 ? 275
Run Code Online (Sandbox Code Playgroud)
我们可以在上面的结果中看到:
测试 3
SELECT [id], [col2]
FROM dbo.OddSort
ORDER BY col2;
Run Code Online (Sandbox Code Playgroud)
返回:
id col2
1 eA
8 éB
4 ëC
7 èD
2 êE
10 ?F
5 EG
3 éH
6 ëh
9 ëH
Run Code Online (Sandbox Code Playgroud)
我们可以在上面的结果中看到:
col2
问题中排序顺序差异的原因再次是由于“多级比较”,而不是“上下文敏感性”。补充说明:
关于获得确切的规则,这并不像它应该的那么容易。获得对这些规则的具体解释的问题在于,Unicode 排序规则虽然明确记录,但仍是建议。由供应商(例如 Microsoft)来实施这些建议。Microsoft 并未完全按照 Unicode 文档中的说明实施这些建议,因此存在脱节(类似于 HTML 或 CSS 规范在供应商之间的实施方式既不是完全的,也不是以相同的方式实施的)。然后,有不同版本的 Windows 排序规则(您使用的100
是 SQL Server 2008 附带的版本)并且绑定到比当前版本的 Unicode 或ICU 排序规则演示更旧的 Unicode 版本正在使用。例如,SQL Server 2008的“排序规则和 Unicode 支持”文档的SQL Server 2008 排序规则中的新增内容部分提出了两个非常有趣的关于_100_
排序规则系列的“新增内容”的观点:
Unicode 5.0 案例表。
Unicode 5.0 于 2006 年 7 月发布(当时字符数据库发布,完整规范于 2006 年底发布)。当前版本是 2017 年 6 月发布的 10.0。鉴于过去 4 年的发布模式,11.0 版本很可能会在 2018 年年中的某个时候发布。
已将权重添加到先前未加权的字符中,这些字符将进行同等比较。
这些权重很可能是在 Unicode 标准中定义的,只是在它的这个实现中没有。
尽管如此,上面链接的 UCA 文档是一个很好的起点。
Windows / .NET / SQL Server 使用的排序键与 Unicode 标准(参见@Patrick 的回答)中显示的或在ICU 中实现的不完全相同。要查看 Windows / .NET / SQL Server 使用什么,您可以尝试CompareInfo.GetSortKey Method。我创建了一个 SQLCLR UDF 来传递这些值并获取排序键。请注意,我在安装了 .NET Framework 4.5 - 4.6.1 的 Windows 10 上使用 SQL Server 2017,因此 .NET应该使用 Unicode 6.0.0。此外,这些字符串未使用 Level4。
SELECT [id], [col3]
FROM dbo.OddSort
ORDER BY col3;
Run Code Online (Sandbox Code Playgroud)
查看测试 1 的这些排序键,并意识到级别像ORDER BY
子句中的多个列一样排序(L3 在 L2 的相同值内排序,L2 在L1 的相同值内排序),应该说明行为的原因问题中提到的实际上是Unicode的多级排序功能。同样地:
id col3
1 e A
8 é B
4 ë C
7 è D
2 ê E
10 ? F
5 E G
3 é H
6 ë h
9 ë H
Run Code Online (Sandbox Code Playgroud)
查看测试 2 的一些排序键,我们可以看到首先排序基本字符 (L1),然后排序重音符号 (L2),然后排序大小写 (L3)。
由于数据类型是NVARCHAR
,我们只关心 Unicode 代码点和排序算法,因此UNICODE()
在测试 1 中使用该函数。虽然代码页由大多数排序规则指定,但它们仅与VARCHAR
数据有关。意思是,虽然代码页 1252 是由Latin1_General*
一系列排序规则指定的,但在这里可以忽略。
默认 Unicode 排序规则元素表 (DUCET)(应映射到系列排序规则的版本 5.0.0)中描述的权重_100_
适用于美国英语,但不适用于其他语言环境/语言。其他语言需要从DUCET开始,然后应用公共区域设置数据存储库 (CLDR) 项目定义的特定于区域设置的覆盖规则。据我所知,2006 年发布了 1.4 / 1.4.1 版本。要获得这些覆盖,请通过http://unicode.org/Public/cldr/1.4.0/core.zip下载 CLDR 1.4“核心”文件,然后,在该 zip 文件中,转到collation文件夹并找到与正在使用的语言环境对应的 XML 文件。这些文件只包含覆盖,而不是完整的整理规则集。
Pat*_*zek 13
这个问题与数据库无关,而是与 Unicode 处理和规则有关。
基于https://docs.microsoft.com/en-us/sql/t-sql/statements/windows-collation-name-transact-sql Latin1_General_100_CS_AS 的意思是:“排序规则使用 Latin1 通用字典排序规则并映射到代码页1252" 加上 CS = Case Sensitive 和 AS = Accent Sensitive。
Windows 代码页 1252 和 Unicode ( http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT )之间的映射为我们正在处理的所有字符显示相同的值(除了 e 与宏这在 Microsoft 映射中不存在,所以不知道它对这种情况有什么作用),所以我们现在可以专注于 Unicode 工具和术语。
首先,让我们准确地知道我们正在处理的所有字符串:
0065 LATIN SMALL LETTER E
0041 LATIN CAPITAL LETTER A
00E9 LATIN SMALL LETTER E WITH ACUTE
0042 LATIN CAPITAL LETTER B
00EB LATIN SMALL LETTER E WITH DIAERESIS
0043 LATIN CAPITAL LETTER C
00E8 LATIN SMALL LETTER E WITH GRAVE
0044 LATIN CAPITAL LETTER D
00EA LATIN SMALL LETTER E WITH CIRCUMFLEX
0045 LATIN CAPITAL LETTER E
?0113 LATIN SMALL LETTER E WITH MACRON
0046 LATIN CAPITAL LETTER F
Run Code Online (Sandbox Code Playgroud)
Unicode 整理算法描述如下:https : //www.unicode.org/reports/tr10/
看看第 1.3 节“上下文敏感度”,它解释了排序不能只依赖一个接一个的字符,因为某些规则是上下文敏感的。
还要注意 1.8 中的这些要点:
排序规则不是字符串的属性。通常,在串联或子字符串操作下不保留整理顺序。
默认情况下,该算法使用三个完全可定制的级别。对于拉丁文字,这些级别大致对应于:
alphabetic ordering
diacritic ordering
case ordering.
Run Code Online (Sandbox Code Playgroud)
但是算法本身有点密集。它的要点是:简而言之,Unicode 校对算法采用输入 Unicode 字符串和校对元素表,其中包含字符的映射数据。它产生一个排序键,它是一个无符号的 16 位整数数组。如此产生的两个或多个排序键然后可以进行二进制比较,以在生成它们的字符串之间进行正确的比较。
您可以在此处查看特定的拉丁语排序规则:http : //developer.mimer.com/collations/charts/latin.htm 或更直接且专门针对 MS SQL:http : //collation-charts.org/mssql/mssql。 0409.1252.Latin1_General_CS_AS.html
对于e
它显示的字符:
e E é É è È ê Ê ë Ë
这解释了您订购时的结果,col1
除了 ? 代码页 1252 中不存在,所以我完全不知道它用它做什么。
或者,如果我们手动执行 Unicode 算法,使用http://www.unicode.org/Public/UCA/latest/allkeys.txt中 DUCET 的键值:
step 1:归一化形式D,所以每种情况都变成:
e => U+0065
é => U+0065 U+0301
ë => U+0065 U+0308
è => U+0065 U+0300
ê => U+0065 U+0302
? => U+0065 U+0304
Run Code Online (Sandbox Code Playgroud)
第 2 步,生成排序规则数组(在文件中查找allkeys.txt
)
e => [.1D10.0020.0002]
é => [.1D10.0020.0002] [.0000.0024.0002]
ë => [.1D10.0020.0002] [.0000.002B.0002]
è => [.1D10.0020.0002] [.0000.0025.0002]
ê => [.1D10.0020.0002] [.0000.0027.0002]
? => [.1D10.0020.0002] [.0000.0032.0002]
Run Code Online (Sandbox Code Playgroud)
步骤 3,表单排序键(对于每个级别,取每个排序规则数组中的每个值,然后将 0000 作为分隔符并重新开始下一级)
e => 1D10 0000 0020 0000 0002
é => 1D10 0000 0020 0024 0000 0002 0002
ë => 1D10 0000 0020 002B 0000 0002 0002
è => 1D10 0000 0020 0025 0000 0002 0002
ê => 1D10 0000 0020 0027 0000 0002 0002
? => 1D10 0000 0020 0032 0000 0002 0002
Run Code Online (Sandbox Code Playgroud)
step 4、Compare sort keys(简单的将每个值一个一个的二进制比较):第四个值就足够把它们全部排序了,所以最后的顺序变成了:
e
é
è
ê
ë
?
Run Code Online (Sandbox Code Playgroud)
以同样的方式订购col2
:
第 1 步:NFD
eA => U+0065 U+0041
éB => U+0065 U+0301 U+0042
ëC => U+0065 U+0308 U+0043
èD => U+0065 U+0300 U+0044
êE => U+0065 U+0302 U+0045
?F => U+0065 U+0304 U+0046
Run Code Online (Sandbox Code Playgroud)
第 2 步:整理数组
eA => [.1D10.0020.0002] [.1CAD.0020.0008]
éB => [.1D10.0020.0002] [.0000.0024.0002] [.1CC6.0020.0008]
ëC => [.1D10.0020.0002] [.0000.002B.0002] [.1CE0.0020.0008]
èD => [.1D10.0020.0002] [.0000.0025.0002] [.1CF5.0020.0008]
êE => [.1D10.0020.0002] [.0000.0027.0002] [.1D10.0020.0008]
?F => [.1D10.0020.0002] [.0000.0032.0002] [.1D4B.0020.0008]
Run Code Online (Sandbox Code Playgroud)
第 3 步:表单排序键
eA => 1D10 1CAD 0000 0020 0020 0000 0002 0008
éB => 1D10 1CC6 0000 0020 0024 0020 0000 0002 0002 0008
ëC => 1D10 1CE0 0000 0020 002B 0020 0000 0002 0002 0008
èD => 1D10 1CF5 0000 0020 0025 0020 0000 0002 0002 0008
êE => 1D10 1D10 0000 0020 0027 0020 0000 0002 0002 0008
?F => 1D10 1D4B 0000 0020 0032 0020 0000 0002 0002 0008
Run Code Online (Sandbox Code Playgroud)
step 4 : 比较排序键:第二个值就足够把它们全部排序了,而且实际上已经是升序了,所以最终的顺序确实是:
eA
éB
ëC
èD
êE
?F
Run Code Online (Sandbox Code Playgroud)
更新:添加 Solomon Rutzky 的第三种情况,由于启用新规则的空间而更加棘手(我选择了“不可忽略的情况”):
第一步,NFD:
è 1 => U+0065 U+0300 U+0020 U+0031
ê 5 => U+0065 U+0302 U+0020 U+0035
e 2 => U+0065 U+0020 U+0032
é 4 => U+0065 U+0301 U+0020 U+0034
? 3 => U+0065 U+0304 U+0020 U+0033
ë 6 => U+0065 U+0308 U+0020 U+0036
Run Code Online (Sandbox Code Playgroud)
第 2 步,生成排序规则数组:
è 1 => [.1D10.0020.0002] [.0000.0025.0002] [*0209.0020.0002] [.1CA4.0020.0002]
ê 5 => [.1D10.0020.0002] [.0000.0027.0002] [*0209.0020.0002] [.1CA8.0020.0002]
e 2 => [.1D10.0020.0002] [*0209.0020.0002] [.1CA5.0020.0002]
é 4 => [.1D10.0020.0002] [.0000.0024.0002] [*0209.0020.0002] [.1CA7.0020.0002]
? 3 => [.1D10.0020.0002] [.0000.0032.0002] [*0209.0020.0002] [.1CA6.0020.0002]
ë 6 => [.1D10.0020.0002] [.0000.002B.0002] [*0209.0020.0002] [.1CA9.0020.0002]
Run Code Online (Sandbox Code Playgroud)
第三步,表单排序键:
è 1 => 1D10 0209 1CA4 0000 0020 0025 0020 0020 0000 0002 0002 0002 0002
ê 5 => 1D10 0209 1CA8 0000 0020 0027 0020 0020 0000 0002 0002 0002 0002
e 2 => 1D10 0209 1CA5 0000 0020 0020 0020 0000 0002 0002 0002
é 4 => 1D10 0209 1CA7 0000 0020 0024 0020 0020 0000 0002 0002 0002 0002
? 3 => 1D10 0209 1CA6 0000 0020 0032 0020 0020 0000 0002 0002 0002 0002
ë 6 => 1D10 0209 1CA9 0000 0020 002B 0020 0020 0000 0002 0002 0002 0002
Run Code Online (Sandbox Code Playgroud)
第 4 步,比较排序键:
基本上第三个值决定了顺序,实际上只是根据最后一位数字,所以顺序应该是:
è 1
e 2
? 3
é 4
ê 5
ë 6
Run Code Online (Sandbox Code Playgroud)
第二次更新基于 Solomon Rutzky 关于 Unicode 版本的评论。
我此时使用了allkeys.txt
最新的Unicode版本的数据,即10.0版
如果我们需要考虑Unicode 5.1,这将是:http : //www.unicode.org/Public/UCA/5.1.0/allkeys.txt
我刚刚检查过,对于上面的所有字符,排序规则数组如下:
e => [.119D.0020.0002.0065]
é => [.119D.0020.0002.0065] [.0000.0032.0002.0301]
ë => [.119D.0020.0002.0065] [.0000.0047.0002.0308]
è => [.119D.0020.0002.0065] [.0000.0035.0002.0300]
ê => [.119D.0020.0002.0065] [.0000.003C.0002.0302]
? => [.119D.0020.0002.0065] [.0000.005B.0002.0304]
Run Code Online (Sandbox Code Playgroud)
和:
eA => [.119D.0020.0002.0065] [.1141.0020.0008.0041]
éB => [.119D.0020.0002.0065] [.0000.0032.0002.0301] [.1157.0020.0008.0042]
ëC => [.119D.0020.0002.0065] [.0000.0047.0002.0308] [.116F.0020.0008.0043]
èD => [.119D.0020.0002.0065] [.0000.0035.0002.0300] [.1182.0020.0008.0044]
êE => [.119D.0020.0002.0065] [.0000.003C.0002.0302] [.119D.0020.0008.0045]
?F => [.119D.0020.0002.0065] [.0000.005B.0002.0304] [.11D5.0020.0008.0046]
Run Code Online (Sandbox Code Playgroud)
和:
è 1 => [.119D.0020.0002.0065] [.0000.0035.0002.0300] [*0209.0020.0002.0020] [.1138.0020.0002.0031]
ê 5 => [.119D.0020.0002.0065] [.0000.003C.0002.0302] [*0209.0020.0002.0020] [.113C.0020.0002.0035]
e 2 => [.119D.0020.0002.0065] [*0209.0020.0002.0020] [.1139.0020.0002.0032]
é 4 => [.119D.0020.0002.0065] [.0000.0032.0002.0301] [*0209.0020.0002.0020] [.113B.0020.0002.0034]
? 3 => [.119D.0020.0002.0065] [.0000.005B.0002.0304] [*0209.0020.0002.0020] [.113A.0020.0002.0033]
ë 6 => [.119D.0020.0002.0065] [.0000.0047.0002.0308] [*0209.0020.0002.0020] [.113D.0020.0002.0036]
Run Code Online (Sandbox Code Playgroud)
然后计算到以下排序键:
e => 119D 0000 0020 0000 0002 0000 0065
é => 119D 0000 0020 0032 0000 0002 0002 0000 0065 0301
ë => 119D 0000 0020 0047 0000 0002 0002 0000 0065 0308
è => 119D 0000 0020 0035 0000 0002 0002 0000 0065 0300
ê => 119D 0000 0020 003C 0000 0002 0002 0000 0065 0302
? => 119D 0000 0020 005B 0000 0002 0002 0000 0065 0304
Run Code Online (Sandbox Code Playgroud)
和:
eA => 119D 1141 0000 0020 0020 0000 0002 0008 0000 0065 0041
éB => 119D 1157 0000 0020 0032 0020 0000 0002 0002 0008 0000 0065 0301 0042
ëC => 119D 116F 0000 0020 0047 0020 0000 0002 0002 0008 0000 0065 0308 0043
èD => 119D 1182 0000 0020 0035 0020 0000 0002 0002 0008 0000 0065 0300 0044
êE => 119D 119D 0000 0020 003C 0020 0000 0002 0002 0008 0000 0065 0302 0045
?F => 119D 11D5 0000 0020 005B 0020 0000 0002 0002 0008 0000 0065 0304 0046
Run Code Online (Sandbox Code Playgroud)
和:
è 1 => 119D 0209 1138 0000 0020 0035 0020 0020 0000 0002 0002 0002 0002 0000 0065 0300 0020 0031
ê 5 => 119D 0209 113C 0000 0020 003C 0020 0020 0000 0002 0002 0002 0002 0000 0065 0302 0020 0035
e 2 => 119D 0209 1139 0000 0020 0020 0020 0000 0002 0002 0002 0000 0065 0020 0032
é 4 => 119D 0209 113B 0000 0020 0032 0020 0020 0000 0002 0002 0002 0002 0000 0065 0301 0020 0034
? 3 => 119D 0209 113A 0000 0020 005B 0020 0020 0000 0002 0002 0002 0002 0000 0065 0304 0020 0033
ë 6 => 119D 0209 113D 0000 0020 0047 0020 0020 0000 0002 0002 0002 0002 0000 0065 0308 0020 0036
Run Code Online (Sandbox Code Playgroud)
这再次给出了这三个排序结果:
e
é
è
ê
ë
?
Run Code Online (Sandbox Code Playgroud)
和
eA
éB
ëC
èD
êE
?F
Run Code Online (Sandbox Code Playgroud)
和
è 1
e 2
? 3
é 4
ê 5
ë 6
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1099 次 |
最近记录: |