10.10.1 Unicode 字符集

本节介绍可用于 Unicode 字符集的排序规则及其区分属性。有关 Unicode 的常规信息,请参见第 10.9 节“ Unicode 支持”

MySQL 支持多种 Unicode 字符集:

  • utf8mb4:Unicode 字符集的 UTF-8 编码,每个字符使用 1-4 个字节。

  • utf8mb3:Unicode 字符集的 UTF-8 编码,每个字符使用一到三个字节。

  • utf8utf8mb3的别名。

  • ucs2:Unicode 字符集的 UCS-2 编码,每个字符使用两个字节。

  • utf16:Unicode 字符集的 UTF-16 编码,每个字符使用两个或四个字节。类似于ucs2,但扩展了辅助字符。

  • utf16le:Unicode 字符集的 UTF-16LE 编码。像utf16,但是小端而不是大端。

  • utf32:Unicode 字符集的 UTF-32 编码,每个字符使用四个字节。

utf8mb4utf16utf16leutf32支持 BMP 之外的基本多语言平面(BMP)字符和补充字符。 utf8ucs2仅支持 BMP 字符。

大多数 Unicode 字符集具有常规排序规则(名称中用_generaltable 示或没有语言说明符),二进制排序规则(名称中用_bintable 示)以及几种特定于语言的排序规则(由语言说明符 table 示)。例如,对于utf8mb4utf8mb4_general_ciutf8mb4_bin是其常规归类和二进制归类,而utf8mb4_danish_ci是其特定于语言的归类之一。

utf16le的排序规则支持是有限的。唯一可用的排序规则是utf16le_general_ciutf16le_bin。这些类似于utf16_general_ciutf16_bin

Unicode 排序算法(UCA)版本

MySQL 根据http://www.unicode.org/reports/tr10/所述的 Unicode 归类算法(UCA)实现xxx_unicode_ci归类。排序规则使用版本 4.0.0 UCA 重键:http://www.unicode.org/Public/UCA/4.0.0/allkeys-4.0.0.txtxxx_unicode_ci归类仅部分支持 Unicode 归类算法。不支持某些字符,并且不完全支持组合标记。这主要影响越南语,约鲁巴语和一些较小的语言,例如 Navajo。在字符串比较中,组合字符被认为与用单个 unicode 字符编写的相同字符不同,并且两个字符被认为具有不同的长度(例如,由CHAR_LENGTH()函数返回或在结果集元数据中)。

基于高于 4.0.0 的 UCA 版本的 Unicode 归类在归类名称中包含该版本。因此,utf8_unicode_520_ci基于 UCA 5.2.0 权重键(http://www.unicode.org/Public/UCA/5.2.0/allkeys.txt)。

LOWER()UPPER()函数根据其参数的排序规则执行大小写折叠。仅当参数归类使用足够高的 UCA 版本时,这些函数才能转换仅在 Unicode 版本高于 4.0.0 中具有大写和小写版本的字符。

Language-Specific Collations

如果仅基于 Unicode 排序规则算法(UCA)的排序不适用于某种语言,则 MySQL 将实现特定于语言的 Unicode 排序规则。特定于语言的归类基于 UCA,并带有其他语言定制规则。此类规则的示例将在本节后面介绍。对于有关特定语言 Sequences 的问题,unicode.orghttp://www.unicode.org/cldr/charts/30/collation/index.html提供了通用语言环境数据存储库(CLDR)整理 table。

下 table 中显示的语言名称 table 示特定于语言的排序规则。 Unicode 字符集可以包括一种或多种这些语言的排序规则。

table10.3 Unicode 归类语言说明符

LanguageLanguage Specifier
Classical Latinroman
Croatiancroatian
Czechczech
Danishdanish
Esperantoesperanto
Estonianestonian
德国电话簿订单german2
Hungarianhungarian
Icelandicicelandic
Latvianlatvian
Lithuanianlithuanian
Persianpersian
Polishpolish
Romanianromanian
Sinhalasinhala
Slovakslovak
Slovenianslovenian
Modern Spanishspanish
Traditional Spanishspanish2
Swedishswedish
Turkishturkish
Vietnamesevietnamese

针对以下克罗地亚字母量身定制了克罗地亚排序规则:ČĆĐLjNjŠŽ

丹麦归类也可用于挪威语。

对于古典拉丁归类,IJ比较相等,而UV比较相等。

西班牙语归类可用于现代和传统西班牙语。对于两者而言,ñ(n-tilde)是no之间的单独字母。另外,对于传统西班牙语,chcd之间的单独字母,而lllm之间的单独字母。

传统的西班牙归类也可以用于阿斯图里亚斯语和加利西亚语。

瑞典归类包括瑞典规则。例如,在瑞典语中,以下关系成立,德语或法语使用者则不会期望这种关系:

Ü = Y < Ö

_general_ci 与_unicode_ci 归类

对于任何 Unicode 字符集,使用xxx_general_ci归类执行的操作都比使用xxx_unicode_ci归类进行的操作要快。例如,对utf8_general_ci归类的比较比对utf8_unicode_ci的比较更快,但正确性稍差。原因是utf8_unicode_ci支持扩展等 Map;也就是说,一个字符与其他字符的组合相等时。例如,在德语和某些其他语言中,ß等于ssutf8_unicode_ci还支持收缩和可忽略字符。 utf8_general_ci是不支持扩展,收缩或可忽略字符的传统排序规则。它只能在字符之间进行一对一比较。

为了进一步说明,在utf8_general_ciutf8_unicode_ci中都具有以下相等性(有关比较或搜索中的效果,请参见第 10.8.6 节“排序规则效果的示例”):

Ä = A
Ö = O
Ü = U

归类之间的不同之处在于utf8_general_ci是正确的:

ß = s

而对于utf8_unicode_ci,这是正确的,它支持德语 DIN-1 排序(也称为字典 Sequences):

ß = ss

如果使用utf8_unicode_ci的排序不适用于某种语言,则 MySQL 将实现utf8语言特定的排序规则。例如,utf8_unicode_ci适用于德语字典 Sequences 和法语,因此无需创建特殊的utf8归类。

utf8_general_ci对德语和法语都令人满意,除了ß等于s而不是ss。如果这对于您的应用程序是可接受的,则应使用utf8_general_ci,因为它速度更快。如果这是不可接受的(例如,如果您需要德语词典 Sequences),请使用utf8_unicode_ci,因为它更准确。

如果您需要 Order 德国 DIN-2(电话簿),请使用utf8_german2_ci归类,它将以下几组字符进行比较:

Ä = Æ = AE
Ö = Œ = OE
Ü = UE
ß = ss

utf8_german2_cilatin1_german2_ci类似,但后者不比较Æ等于AEŒ等于OE。因为utf8_general_ci就足够了,所以对于德国词典 Sequences,没有utf8_german_ci对应于latin1_german_ci

字符整理权重

角色的归类权重确定如下:

  • 对于除_bin(二进制)归类外的所有 Unicode 归类,MySQL 执行 table 查找以查找字符的归类权重。

  • 对于_bin归类,权重基于代码点,可能添加了前导零字节。

可以使用WEIGHT_STRING()功能显示整理权重。 (请参阅第 12.7 节“字符串函数和运算符”。)如果排序规则使用权重查找 table,但是 table 中没有字符(例如,因为它是“新”字符),则排序权重确定变得更加复杂:

  • 对于一般归类(xxx_general_ci)中的 BMP 字符,权重是代码点。

  • 对于 UCA 归类中的 BMP 字符(例如xxx_unicode_ci和特定于语言的归类),适用以下算法:

if (code >= 0x3400 && code <= 0x4DB5)
  base= 0xFB80; /* CJK Ideograph Extension */
else if (code >= 0x4E00 && code <= 0x9FA5)
  base= 0xFB40; /* CJK Ideograph */
else
  base= 0xFBC0; /* All other characters */
aaaa= base +  (code >> 15);
bbbb= (code & 0x7FFF) | 0x8000;

结果是两个整理元素的序列,aaaa后跟bbbb。例如:

mysql> SELECT HEX(WEIGHT_STRING(_ucs2 0x04CF COLLATE ucs2_unicode_ci));
+----------------------------------------------------------+
| HEX(WEIGHT_STRING(_ucs2 0x04CF COLLATE ucs2_unicode_ci)) |
+----------------------------------------------------------+
| FBC084CF                                                 |
+----------------------------------------------------------+

因此,在所有 UCA 4.0.0 归类中,U+04cf CYRILLIC SMALL LETTER PALOCHKA大于U+04c0 CYRILLIC LETTER PALOCHKA。使用 UCA 5.2.0 归类,所有 palochka 都可以排序。

  • 对于一般归类中的补充字符,权重是0xfffd REPLACEMENT CHARACTER的权重。对于 UCA 4.0.0 归类中的补充字符,其归类权重为0xfffd。也就是说,对于 MySQL,所有补充字符都彼此相等,并且几乎大于所有 BMP 字符。

一个带有 Deseret 字符和COUNT(DISTINCT)的示例:

CREATE TABLE t (s1 VARCHAR(5) CHARACTER SET utf32 COLLATE utf32_unicode_ci);
INSERT INTO t VALUES (0xfffd);   /* REPLACEMENT CHARACTER */
INSERT INTO t VALUES (0x010412); /* DESERET CAPITAL LETTER BEE */
INSERT INTO t VALUES (0x010413); /* DESERET CAPITAL LETTER TEE */
SELECT COUNT(DISTINCT s1) FROM t;

结果为 2,因为在 MySQL xxx_unicode_ci归类中,替换字符的权重为0x0dc6,而 Deseret Bee 和 Deseret Tee 的权重均为0xfffd。 (如果使用utf32_general_ci归类,则结果为 1,因为在该归类中所有三个字符的权重均为0xfffd.)

一个带有楔形 Literals 和WEIGHT_STRING()的示例:

/*
The four characters in the INSERT string are
00000041  # LATIN CAPITAL LETTER A
0001218F  # CUNEIFORM SIGN KAB
000121A7  # CUNEIFORM SIGN KISH
00000042  # LATIN CAPITAL LETTER B
*/
CREATE TABLE t (s1 CHAR(4) CHARACTER SET utf32 COLLATE utf32_unicode_ci);
INSERT INTO t VALUES (0x000000410001218f000121a700000042);
SELECT HEX(WEIGHT_STRING(s1)) FROM t;

结果是:

0E33 FFFD FFFD 0E4A

0E330E4AUCA 4.0.0中的主要权重。 FFFD是 KAB 和 KISH 的权重。

所有补充字符都相等的规则不是最佳的,但是不会引起麻烦。这些字符非常少见,因此由多字符组成的字符串完全由补充字符组成非常罕见。在日本,由于辅助字符是晦涩的汉字 table 意 Literals,因此一般用户根本不在乎它们的 Sequences。如果您确实希望按 MySQL 规则对行进行排序,然后再按代码点值对行进行排序,则很简单:

ORDER BY s1 COLLATE utf32_unicode_ci, s1 COLLATE utf32_bin
  • 对于基于高于 4.0.0 的 UCA 版本的补充字符(例如xxx_unicode_520_ci),补充字符不一定都具有相同的整理重量。有些具有来自 UCA allkeys.txt文件的明确权重。其他人具有根据此算法计算的权重:
aaaa= base +  (code >> 15);
bbbb= (code & 0x7FFF) | 0x8000;

在“按字符的代码值排序”和“按字符的二进制 table 示形式排序”之间存在差异,这是由于代理而仅在utf16_bin上出现的差异。

假设utf16_bin(utf16的二进制排序规则)是“逐字节”而不是“逐字符”的二进制比较。如果是这样,则utf16_bin中的字符 Sequences 将与utf8_bin中的字符 Sequences 不同。例如,下图显示了两个稀有字符。第一个字符在E000-FFFF的范围内,因此它大于替代字符但小于辅助字符。第二个字符是补充字符。

Code point  Character                    utf8         utf16
----------  ---------                    ----         -----
0FF9D       HALFWIDTH KATAKANA LETTER N  EF BE 9D     FF 9D
10384       UGARITIC LETTER DELTA        F0 90 8E 84  D8 00 DF 84

图 table 中的两个字符按代码点值的 Sequences 排列,因为0xff9d < 0x10384 . And they are in order by utf8 value because 0xef < 0xf0 . But they are not in order by utf16 value, if we use byte-by-byte comparison, because 0xff > 0xd8

因此,MySQL 的utf16_bin排序规则不是“逐字节”。这是“按代码点”。当 MySQL 在utf16中看到辅助字符编码时,它将转换为字符的代码点值,然后进行比较。因此,utf8_binutf16_bin是相同的 Sequences。这与 UCS_BASIC 排序规则的 SQL:2008 标准要求一致:“ UCS_BASIC 是一种排序规则,其中排序完全由要排序的字符串中字符的 Unicode 标量值确定。它适用于 UCS 字符库。由于每个字符库都是 UCS 库的子集,因此 UCS_BASIC 排序规则可能适用于每个字符集。注 11:字符的 Unicode 标量值是其代码点,被视为无符号整数。”

如果字符集是ucs2,则比较是逐字节进行的,但是ucs2字符串无论如何都不应包含替代。

Miscellaneous Information

xxx_general_mysql500_ci归类保留原始xxx_general_ci归类在 5.1.24 之前的 Sequences,并允许对在 MySQL 5.1.24 之前创建的 table 进行升级(缺陷号 27877)。