重音敏感排序

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)允许进行复杂的多级排序。进一步来说:

  1. 排序不是比较:

    确定两个字符串是相同还是不同是相当简单的(给定特定的区域设置/语言和一组敏感性)。但是确定 2 个或更多字符串的顺序可能非常复杂。

  2. 排序是通过一系列步骤完成的,每一步都应用于整个字符串,而不是逐个字符:

    1. 标准:排序基本字符(不考虑重音和大小写差异)
    2. 如果重音敏感,应用重音/变音符号权重
    3. IF 区分大小写,应用外壳权重

当您按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)

我们可以在上面的结果中看到:

  1. 代码点未确定排序顺序
  2. 非重音字符排在重音字符之前(在同一个字母中:f仍然会在所有这些字符之后)。显然,重音权重应用在案例权重之前。
  3. 在相同的重音(或非重音)字符(即e然后E,然后ë然后Ë)中,小写在大写之前排序。大多数 Windows 排序规则都使用这种定制,而大多数 SQL Server 排序规则首先对大写进行排序。

测试 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)

我们可以在上面的结果中看到:

  1. 一级排序确实是基本字符。如果是重音符号/变音符号,那么ëC (id = 4) 、? F (id = 10) 和EG (id = 5) 行就不会是它们所在的位置。如果它是套管,那么EG (id = 5) 行就不会是它所在的位置。
  2. 真正的二级排序是重音/变音符号。这解释了为什么最后三行是éH -> ëh -> ëH而不是ëh -> éH -> ëH(即 ID 3 -> 6 -> 9 而不是 6 -> 3 -> 9)。
  3. 真正的三级排序是外壳。这就是为什么最后 2 行是ëh -> ëH,因为小写排在第一位。

测试 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)

我们可以在上面的结果中看到:

  1. 排序顺序与测试2完全相同。这里测试值的唯一区别是每个字符之间有一个空格,消除了上下文规则的可能性。因此,我们知道col2问题中排序顺序差异的原因再次是由于“多级比较”,而不是“上下文敏感性”。

补充说明:

  1. 关于获得确切的规则,这并不像它应该的那么容易。获得对这些规则的具体解释的问题在于,Unicode 排序规则虽然明确记录,但仍是建议。由供应商(例如 Microsoft)来实施这些建议。Microsoft 并未完全按照 Unicode 文档中的说明实施这些建议,因此存在脱节(类似于 HTML 或 CSS 规范在供应商之间的实施方式既不是完全的,也不是以相同的方式实施的)。然后,有不同版本的 Windows 排序规则(您使用的100是 SQL Server 2008 附带的版本)并且绑定到比当前版本的 Unicode 或ICU 排序规则演示更旧的 Unicode 版本正在使用。例如,SQL Server 2008“排序规则和 Unicode 支持”文档的SQL Server 2008 排序规则中的新增内容部分提出了两个非常有趣的关于_100_排序规则系列的“新增内容”的观点:

    1. Unicode 5.0 案例表。

      Unicode 5.0 于 2006 年 7 月发布(当时字符数据库发布,完整规范于 2006 年底发布)。当前版本是 2017 年 6 月发布的 10.0。鉴于过去 4 年的发布模式,11.0 版本很可能会在 2018 年年中的某个时候发布。

    2. 已将权重添加到先前未加权的字符中,这些字符将进行同等比较。

      这些权重很可能是在 Unicode 标准中定义的,只是在它的这个实现中没有。

     
    尽管如此,上面链接的 UCA 文档是一个很好的起点。

  2. 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)。

  3. 由于数据类型是NVARCHAR,我们只关心 Unicode 代码点和排序算法,因此UNICODE()在测试 1 中使用该函数。虽然代码页由大多数排序规则指定,但它们仅与VARCHAR数据有关。意思是,虽然代码页 1252 是由Latin1_General*一系列排序规则指定的,但在这里可以忽略。

  4. 默认 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 文件中,转到collat​​ion文件夹并找到与正在使用的语言环境对应的 XML 文件。这些文件只包含覆盖,而不是完整的整理规则集。


Pat*_*zek 13

这个问题与数据库无关,而是与 Unicode 处理和规则有关。

基于https://docs.microsoft.com/en-us/sql/t-sql/statements/windows-collat​​ion-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/collat​​ions/charts/latin.htm 或更直接且专门针对 MS SQL:http : //collat​​ion-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)