本节介绍 Unicode 字符集可用的排序规则及其差异化属性。有关 Unicode 的一般信息,请参阅 第 12.9 节,“Unicode 支持”。
MySQL 支持多个 Unicode 字符集
utf8mb4: 使用每个字符 1 到 4 个字节的 Unicode 字符集的 UTF-8 编码。utf8mb3: 使用每个字符 1 到 3 个字节的 Unicode 字符集的 UTF-8 编码。此字符集已弃用;请改用utf8mb4。utf8:utf8mb3的弃用别名。请改用utf8mb4。注意预计在将来的版本中,
utf8将成为utf8mb4的别名。ucs2: 使用每个字符 2 个字节的 Unicode 字符集的 UCS-2 编码。已弃用;预计在 MySQL 的未来版本中将删除对该字符集的支持。utf16: Unicode 字符集的 UTF-16 编码,使用每个字符 2 或 4 个字节。与ucs2相似,但扩展了对补充字符的支持。utf16le: Unicode 字符集的 UTF-16LE 编码。与utf16相似,但小端而不是大端。utf32: 使用每个字符 4 个字节的 Unicode 字符集的 UTF-32 编码。
utf8mb3 字符集已弃用,预计将在 MySQL 的未来版本中删除。请改用 utf8mb4。目前,utf8 是 utf8mb3 的别名,但现在也已弃用,预计 utf8 随后将成为对 utf8mb4 的引用。utf8mb3 也会在信息模式表中的列以及 SQL SHOW 语句的输出中显示为 utf8 的替代。
为了避免对 utf8 含义的歧义,请考虑为字符集引用显式指定 utf8mb4。
utf8mb4、utf16、utf16le 和 utf32 支持基本多语言平面 (BMP) 字符和位于 BMP 之外的补充字符。 utf8mb3 和 ucs2 只支持 BMP 字符。
大多数 Unicode 字符集都具有通用排序规则(在名称中用 _general 表示,或没有语言说明符),二进制排序规则(在名称中用 _bin 表示),以及多个特定语言的排序规则(用语言说明符表示)。例如,对于 utf8mb4,utf8mb4_general_ci 和 utf8mb4_bin 是其通用排序规则和二进制排序规则,而 utf8mb4_danish_ci 是其特定语言的排序规则之一。
大多数字符集都有一个二进制排序规则。 utf8mb4 是一种例外,它有两个:utf8mb4_bin 和 utf8mb4_0900_bin。这两个二进制排序规则具有相同的排序顺序,但通过其填充属性和排序权重特征来区分。请参阅 排序规则填充属性 和 字符排序权重。
对 utf16le 的排序规则支持有限。可用的排序规则只有 utf16le_general_ci 和 utf16le_bin。它们与 utf16_general_ci 和 utf16_bin 相似。
MySQL 根据 http://www.unicode.org/reports/tr10/ 中描述的 Unicode 排序算法 (UCA) 实现 排序规则。排序规则使用版本 4.0.0 UCA 权重键:http://www.unicode.org/Public/UCA/4.0.0/allkeys-4.0.0.txt。 xxx_unicode_ci 排序规则对 Unicode 排序算法的支持仅是部分的。某些字符不受支持,组合标记也不完全支持。这会影响越南语、约鲁巴语和纳瓦霍语等语言。组合字符在字符串比较中被视为与使用单个 Unicode 字符写入的相同字符不同,并且这两个字符被认为具有不同的长度(例如,由 xxx_unicode_ciCHAR_LENGTH() 函数返回,或在结果集元数据中)。
基于高于 4.0.0 的 UCA 版本的 Unicode 排序规则在排序规则名称中包含版本。示例
utf8mb4_unicode_520_ci基于 UCA 5.2.0 权重键(http://www.unicode.org/Public/UCA/5.2.0/allkeys.txt),utf8mb4_0900_ai_ci基于 UCA 9.0.0 权重键(http://www.unicode.org/Public/UCA/9.0.0/allkeys.txt)。
LOWER() 和 UPPER() 函数根据其参数的排序规则执行大小写折叠。只有在高于 4.0.0 的 Unicode 版本中具有大写和小写版本的字符,才会在这些函数中进行转换,前提是参数排序规则使用足够高的 UCA 版本。
基于 UCA 9.0.0 及更高版本的排序规则比基于 UCA 9.0.0 之前版本的排序规则更快。它们还具有 NO PAD 的填充属性,而基于 UCA 9.0.0 之前版本的排序规则则使用 PAD SPACE。对于非二进制字符串的比较,NO PAD 排序规则会将字符串末尾的空格视为任何其他字符(请参阅 比较中的尾随空格处理)。
要确定排序规则的填充属性,请使用 INFORMATION_SCHEMA COLLATIONS 表,该表具有 PAD_ATTRIBUTE 列。例如
mysql> SELECT COLLATION_NAME, PAD_ATTRIBUTE
FROM INFORMATION_SCHEMA.COLLATIONS
WHERE CHARACTER_SET_NAME = 'utf8mb4';
+----------------------------+---------------+
| COLLATION_NAME | PAD_ATTRIBUTE |
+----------------------------+---------------+
| utf8mb4_general_ci | PAD SPACE |
| utf8mb4_bin | PAD SPACE |
| utf8mb4_unicode_ci | PAD SPACE |
| utf8mb4_icelandic_ci | PAD SPACE |
...
| utf8mb4_0900_ai_ci | NO PAD |
| utf8mb4_de_pb_0900_ai_ci | NO PAD |
| utf8mb4_is_0900_ai_ci | NO PAD |
...
| utf8mb4_ja_0900_as_cs | NO PAD |
| utf8mb4_ja_0900_as_cs_ks | NO PAD |
| utf8mb4_0900_as_ci | NO PAD |
| utf8mb4_ru_0900_ai_ci | NO PAD |
| utf8mb4_ru_0900_as_cs | NO PAD |
| utf8mb4_zh_0900_as_cs | NO PAD |
| utf8mb4_0900_bin | NO PAD |
+----------------------------+---------------+比较没有填充字符串值的 (CHAR, VARCHAR 和 TEXT),这些值在 NO PAD 排序规则下,与其他排序规则在尾部空格方面有所不同。例如,'a' 和 'a ' 比较起来是不同的字符串,而不是同一个字符串。这可以通过使用 utf8mb4 的二进制排序规则看到。 utf8mb4_bin 的填充属性是 PAD SPACE,而 utf8mb4_0900_bin 的填充属性是 NO PAD。因此,涉及 utf8mb4_0900_bin 的操作不会添加尾部空格,并且涉及带尾部空格的字符串的比较在两种排序规则下可能会有所不同。
mysql> CREATE TABLE t1 (c CHAR(10) COLLATE utf8mb4_bin);
Query OK, 0 rows affected (0.03 sec)
mysql> INSERT INTO t1 VALUES('a');
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM t1 WHERE c = 'a ';
+------+
| c |
+------+
| a |
+------+
1 row in set (0.00 sec)
mysql> ALTER TABLE t1 MODIFY c CHAR(10) COLLATE utf8mb4_0900_bin;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM t1 WHERE c = 'a ';
Empty set (0.00 sec)
如果仅基于 Unicode 排序算法 (UCA) 的排序对某种语言不起作用,MySQL 将实现特定语言的 Unicode 排序规则。特定语言的排序规则是基于 UCA 的,并带有额外的语言定制规则。这类规则的示例将在本节后面介绍。有关特定语言排序的疑问,请访问 http://unicode.org,它在 http://www.unicode.org/cldr/charts/30/collation/index.html 提供通用语言数据存储库 (CLDR) 排序图表。
例如,非特定语言的 utf8mb4_0900_ai_ci 和特定语言的 utf8mb4_ Unicode 排序规则都具有以下特点LOCALE_0900_ai_ci
排序规则基于 UCA 9.0.0 和 CLDR v30,不区分重音和不区分大小写。这些特点由排序规则名称中的
_0900、_ai和_ci表示。例外情况:utf8mb4_la_0900_ai_ci不是基于 CLDR 的,因为古典拉丁语在 CLDR 中没有定义。排序规则适用于范围 [U+0, U+10FFFF] 内的所有字符。
如果排序规则不是特定语言的,它会按默认顺序对所有字符(包括补充字符)进行排序(如下所述)。如果排序规则是特定语言的,它会根据特定语言的规则对该语言的字符进行正确排序,并按默认顺序对不在该语言中的字符进行排序。
默认情况下,排序规则会根据 DUCET 表(默认 Unicode 排序元素表)中列出的代码点的字符,按照表中分配的权重值进行排序。排序规则会使用其隐式权重值对 DUCET 表中没有列出的代码点的字符进行排序,这些隐式权重值是根据 UCA 构建的。
对于非特定语言的排序规则,缩略词序列中的字符被视为独立字符。对于特定语言的排序规则,缩略词可能会改变字符排序顺序。
包含以下表格中显示的区域设置代码或语言名称的排序规则名称是特定语言的排序规则。Unicode 字符集可能包含针对以下一种或多种语言的排序规则。
表 12.3 Unicode 排序语言说明符
| 语言 | 语言说明符 |
|---|---|
| 波斯尼亚语 | bs |
| 保加利亚语 | bg |
| 中文 | zh |
| 古典拉丁语 | la 或 roman |
| 克罗地亚语 | hr 或 croatian |
| 捷克语 | cs 或 czech |
| 丹麦语 | da 或 danish |
| 世界语 | eo 或 esperanto |
| 爱沙尼亚语 | et 或 estonian |
| 加利西亚语 | gl |
| 德语电话簿顺序 | de_pb 或 german2 |
| 匈牙利语 | hu 或 hungarian |
| 冰岛语 | is 或 icelandic |
| 日语 | ja |
| 拉脱维亚语 | lv 或 latvian |
| 立陶宛语 | lt 或 lithuanian |
| 蒙古语 | mn |
| 挪威语/博克马尔 | nb |
| 挪威语/新挪威语 | nn |
| 波斯语 | persian |
| 波兰语 | pl 或 polish |
| 罗马尼亚语 | ro 或 romanian |
| 俄语 | ru |
| 塞尔维亚语 | sr |
| 僧伽罗语 | sinhala |
| 斯洛伐克语 | sk 或 slovak |
| 斯洛文尼亚语 | sl 或 slovenian |
| 现代西班牙语 | es 或 spanish |
| 传统西班牙语 | es_trad 或 spanish2 |
| 瑞典语 | sv 或 swedish |
| 土耳其语 | tr 或 turkish |
| 越南语 | vi 或 vietnamese |
MySQL 提供保加利亚语排序规则 utf8mb4_bg_0900_ai_ci 和 utf8mb4_bg_0900_as_cs。
克罗地亚语排序规则是针对以下克罗地亚字母定制的:Č、Ć、Dž、Đ、Lj、Nj、Š、Ž。
当这些语言使用拉丁字母书写时,MySQL 提供 utf8mb4_sr_latn_0900_ai_ci 和 utf8mb4_sr_latn_0900_as_cs 排序规则用于塞尔维亚语,以及 utf8mb4_bs_0900_ai_ci 和 utf8mb4_bs_0900_as_cs 排序规则用于波斯尼亚语。
MySQL 提供两种主要挪威语变体的排序规则:对于博克马尔,可以使用 utf8mb4_nb_0900_ai_ci 和 utf8mb4_nb_0900_as_cs;对于新挪威语,MySQL 现在提供 utf8mb4_nn_0900_ai_ci 和 utf8mb4_nn_0900_as_cs。
对于日语,utf8mb4 字符集包含 utf8mb4_ja_0900_as_cs 和 utf8mb4_ja_0900_as_cs_ks 排序规则。这两种排序规则都区分重音和区分大小写。 utf8mb4_ja_0900_as_cs_ks 还区分平假名字符和片假名字符,而 utf8mb4_ja_0900_as_cs 在排序时将片假名字符和平假名字符视为相等。需要日语排序规则但不需要区分平假名的应用程序可以使用 utf8mb4_ja_0900_as_cs 来获得更好的排序性能。 utf8mb4_ja_0900_as_cs 使用三个权重级别进行排序;utf8mb4_ja_0900_as_cs_ks 使用四个。
对于不区分重音的古典拉丁语排序规则,I 和 J 比较起来是相等的,而 U 和 V 比较起来是相等的。 I 和 J,以及 U 和 V 在基本字母级别上比较起来是相等的。换句话说,J 被视为带重音的 I,而 U 被视为带重音的 V。
MySQL 提供使用西里尔字母书写的蒙古语的排序规则,即 utf8mb4_mn_cyrl_0900_ai_ci 和 utf8mb4_mn_cyrl_0900_as_cs。
西班牙语排序规则适用于现代西班牙语和传统西班牙语。在这两种语言中,ñ(带波浪线的 n)是在 n 和 o 之间的独立字母。此外,对于传统西班牙语,ch 是 c 和 d 之间的独立字母,而 ll 是 l 和 m 之间的独立字母。
传统西班牙语排序规则也可以用于阿斯图里亚斯语和加利西亚语。MySQL 还提供加利西亚语的排序规则,即 utf8mb4_gl_0900_ai_ci 和 utf8mb4_gl_0900_as_cs。(这些排序规则分别与 utf8mb4_es_0900_ai_ci 和 utf8mb4_es_0900_as_cs 相同。)
瑞典语排序规则包含瑞典语规则。例如,在瑞典语中,存在以下关系,而德语或法语使用者不会想到这种关系
Ü = Y < Ö
对于任何 Unicode 字符集,使用 排序规则执行的操作比使用 xxx_general_ci 排序规则执行的操作速度更快。例如,使用 xxx_unicode_ciutf8mb4_general_ci 排序规则进行比较的速度更快,但准确性略低,而使用 utf8mb4_unicode_ci 进行比较的速度略慢,但准确性更高。原因是 utf8mb4_unicode_ci 支持映射,例如扩展;也就是说,当一个字符被认为与其他字符的组合相等时。例如,在德语和其他一些语言中,ß 等于 ss。 utf8mb4_unicode_ci 还支持缩略词和可忽略字符。 utf8mb4_general_ci 是一个旧的排序规则,不支持扩展、缩略词或可忽略字符。它只能对字符进行一对一比较。
为了进一步说明,以下等式在 utf8mb4_general_ci 和 utf8mb4_unicode_ci 中都成立(有关其在比较或搜索中的效果,请参阅 第 12.8.6 节,“排序规则的影响示例”)
Ä = A
Ö = O
Ü = U排序规则之间的差异在于,对于 utf8mb4_general_ci,以下情况成立
ß = s而对于支持德语 DIN-1 排序规则(也称为词典排序规则)的 utf8mb4_unicode_ci,以下情况成立
ß = ss如果使用 utf8mb4_unicode_ci 的排序对某种语言不起作用,MySQL 将实现特定语言的 Unicode 排序规则。例如,utf8mb4_unicode_ci 适用于德语词典排序规则和法语,因此无需创建特殊的 utf8mb4 排序规则。
utf8mb4_general_ci 也适用于德语和法语,只是 ß 等于 s,而不是 ss。如果您的应用程序可以接受这种情况,您应该使用 utf8mb4_general_ci,因为它速度更快。如果不可接受(例如,如果您需要德语词典排序规则),请使用 utf8mb4_unicode_ci,因为它更准确。
如果您需要德语 DIN-2(电话簿)排序规则,请使用 utf8mb4_german2_ci 排序规则,它将以下字符集比较为相等
Ä = Æ = AE
Ö = Œ = OE
Ü = UE
ß = ss
utf8mb4_german2_ci 与 latin1_german2_ci 类似,但后者不会将 Æ 比较为等于 AE 或者将 Œ 比较为等于 OE。没有与德语词典排序规则的 latin1_german_ci 相对应的 utf8mb4_german_ci,因为 utf8mb4_general_ci 足以满足此需求。
字符的排序权重按以下方式确定
对于所有 Unicode 排序规则(除
_bin(二进制)排序规则外),MySQL 会执行表格查找以找到字符的排序权重。对于
_bin排序规则(除utf8mb4_0900_bin外),权重基于代码点,可能会添加前导零字节。对于
utf8mb4_0900_bin,权重是utf8mb4编码字节。排序顺序与utf8mb4_bin相同,但速度要快得多。
可以使用 WEIGHT_STRING() 函数显示排序权重。(请参阅 第 14.8 节,“字符串函数和运算符”。)如果排序规则使用权重查找表,但字符不在表中(例如,因为它是一个“新的”字符),则排序权重确定过程将变得更加复杂
对于一般排序规则 (
) 中的 BMP 字符,权重是代码点。xxx_general_ci对于 UCA 排序规则(例如,
和特定语言的排序规则)中的 BMP 字符,以下算法适用xxx_unicode_ciif (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 | +----------------------------------------------------------+因此,
U+04cf CYRILLIC SMALL LETTER PALOCHKA(ӏ) 在所有 UCA 4.0.0 排序规则中都大于U+04c0 CYRILLIC LETTER PALOCHKA(Ӏ)。在 UCA 5.2.0 排序规则中,所有 палочка 都会一起排序。在通用排序中,辅助字符的权重与
0xfffd REPLACEMENT CHARACTER的权重相同。在 UCA 4.0.0 排序中,辅助字符的排序权重为0xfffd。也就是说,对 MySQL 而言,所有辅助字符彼此相等,且大于几乎所有 BMP 字符。Deseret 字符和
排序中的xxx_unicode_ciCOUNT(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_ci0x0dc6,而 Deseret Bee 和 Deseret Tee 的权重均为0xfffd。(如果使用utf32_general_ci排序,则结果为 1,因为在这项排序中所有三个字符的权重均为0xfffd。)楔形文字和
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 0E4A0E33和0E4A是主要权重,如 UCA 4.0.0 所示。FFFD是 KAB 和 KISH 的权重。所有辅助字符彼此相等的规则并非最佳,但预计不会造成麻烦。这些字符非常罕见,因此由辅助字符组成的多字符字符串也非常罕见。在日本,由于辅助字符是不常用的汉字,因此一般用户并不关心它们排序的顺序。如果您确实需要按 MySQL 规则排序,并以代码点值为次要排序,则可以轻松实现
ORDER BY s1 COLLATE utf32_unicode_ci, s1 COLLATE utf32_bin对于基于高于 4.0.0 的 UCA 版本的辅助字符(例如
),辅助字符并不一定都具有相同的排序权重。一些字符在 UCA 的xxx_unicode_520_ciallkeys.txt文件中具有明确的权重。其他字符的权重则根据以下算法计算得出aaaa= base + (code >> 15); bbbb= (code & 0x7FFF) | 0x8000;
“按字符的代码值排序”与“按字符的二进制表示排序”之间存在差异,这种差异仅在使用 utf16_bin 时出现,这是由于代理项的存在。
假设 utf16_bin(utf16 的二进制排序)是按“字节逐字节”而非“字符逐字符”进行的二进制比较。如果真是这样,则 utf16_bin 中的字符顺序将与 utf8mb4_bin 中的顺序不同。例如,下表显示了两个罕见的字符。第一个字符位于 E000-FFFF 范围内,因此它大于代理项,但小于辅助项。第二个字符是辅助项。
Code point Character utf8mb4 utf16
---------- --------- ------- -----
0FF9D HALFWIDTH KATAKANA LETTER N EF BE 9D FF 9D
10384 UGARITIC LETTER DELTA F0 90 8E 84 D8 00 DF 84由于 0xff9d < 0x10384,因此表中的两个字符按代码点值排序。由于 0xef < 0xf0,因此它们也按 utf8mb4 值排序。但是,如果使用字节逐字节比较,则它们不会按 utf16 值排序,因为 0xff > 0xd8。
因此,MySQL 的 utf16_bin 排序并非“字节逐字节”的。而是“按代码点”的。当 MySQL 在 utf16 中看到辅助字符编码时,它会将其转换为该字符的代码点值,然后进行比较。因此,utf8mb4_bin 和 utf16_bin 的排序相同。这与 SQL:2008 标准对 UCS_BASIC 排序的要求一致:“UCS_BASIC 是一种排序,其中排序完全由要排序的字符串中字符的 Unicode 标量值决定。它适用于 UCS 字符集。由于每个字符集都是 UCS 字符集的子集,因此 UCS_BASIC 排序可能适用于所有字符集。注意 11:字符的 Unicode 标量值是将其代码点视为无符号整数处理的结果。”
如果字符集为 ucs2,则比较按字节逐字节进行,但 ucs2 字符串不应该包含代理项。