说明
本文总结了 Db2 在不同字符集(character set)和校对(collation)设置情况下的排序和比较行为。下面的标题表示在 CREATE DATABASE 语法中的指定。
根据参考9,如果创建数据库的时候没有指定 COLLATE USING,那么默认的 collate 是 system,规则如下:
For non-Unicode databases, the collating sequence is based on the database territory. For Unicode databases, this parameter maps to a language-aware collation, based on the client code set and territory. If an appropriate language-aware collation is turned off, then the IDENTITY collation is used.
创建数据库的时候也可以指定某一种特定的 collate,对于 Unicode 字符集的数据库来说,Db2 提供了三种 collate:IDENTITY collation, language-aware collation, 和 locale-sensitive UCA-based collation.(参考10)
using codeset GBK territory CN
非 Unicode 字符集,不指定 collation 的情况下,默认是 system,collation 由 codeset 和 territoy 决定,这里 territoy 是 CN,根据参考1,使用 SYSTEM_1386(参考2),表示单级比较,先比较字母顺序,再比较大小写,这里字母 j 的顺序在 J 的前面,因此排序是这样的:
db2 get db cfg for test1|grep -i "collating sequence"
Database collating sequence = UNIQUE
db2 "select * from test1 order by c1"
C1
----------
china
China
j2
j6
J2
J6
6 record(s) selected.
using codeset GBK territory CN collate using identity
非 Unicode 字符集,指定 collation 是 identity,也就是按照 codeset 编码,这时候根据 GBK 码表,大写在小写的前面,也就是:
db2 get db cfg for test2|grep -i "collating sequence"
Database collating sequence = IDENTITY
db2 "select * from test2 order by c1"
C1
----------
China
J2
J6
china
j2
j6
6 record(s) selected.
注:GBK identity 和 MySQL 的 gbk_bin 排序结果是一样的。
using codeset UTF-8 territory CN
Unicode 字符集,不指定 collation 的情况下,默认会根据 client codeset 和 territory 决定(文章第二段阐述的规则),假设 client codeset 也是 Unicode,根据参考3,collation 是 identity,因此根据 UTF-8 码表,大写在小写的前面,于是排序是:
db2 get db cfg for test2|grep -i "collating sequence"
Database collating sequence = IDENTITY
db2 "select * from test3 order by c1"
C1
----------
China
J2
J6
china
j2
j6
6 record(s) selected.
注:UTF-8 identity 和 MySQL 的 utf8mb4_bin 排序结果是一样的。
using codeset UTF-8 territory CN collate using SYSTEM_1386_CN
Unicode 字符集,指定了 language-aware-collation 为 SYSTEM_1386_CN,就会按照参考2 进行排序,先比较字母顺序,再比较大小写,也就是:
db2 get db cfg for test4|grep -i "collating sequence"
Database collating sequence = SYSTEM_1386
db2 "select * from test3 order by c1"
C1
----------
china
China
j2
j6
J2
J6
6 record(s) selected.
using codeset UTF-8 territory CN collate using CLDR2701_LZH_KUNIHAN
Unicode 字符集,指定了 locale-sensitive-collation 为 CLDR2701_LZH_KUNIHAN,根据参考4,知道这是一种 UCA 算法(多级排序),默认的排序规则是 CLDR2701_LZH_KUNIHAN_AN_CX_EX_FX_HX_NX_S3,输出是这样的:
db2 get db cfg for test5|grep -i "collating sequence"
Database collating sequence = CLDR2701_LZH_KUNIHAN_AN_CX_EX_FX_HX_NX_S3
db2 "select * from test3 order by c1"
C1
----------
china
China
j2
J2
j6
J6
6 record(s) selected.
可以看到,这是和 SYSTEM_1386 以及 identity 都不同的一种排序方式,这是因为属性 CLDR2701_LZH_KUNIHAN_AN_CX_EX_FX_HX_NX_S3 中的 S3 表示不忽略重音符和大小写,EX 表示关闭大小写级别,CX 表示如果字符串没有其他差别,那么是小写优先,这些属性组合在一起便是:
S3 区分大小写,EX 不区分大小写,于是区分大小写,因为没有其他差别,根据小写优先,所以 china 在 China 的前面,j2 在 J2 的前面,j6 在 J6 的前面。
注意这里非常重要的一点是,为什么小写优先,而 J2 会在 j6 的前面呢?这是因为 UCA 算法是多级比较(Multi-Level Comparison,参考5 和参考6),通俗地讲就是先不考虑大小写和重音,比较两个字符串里的字符,其次才根据是否忽略大小写和重音的属性规则来进行排序。
使用 COLLATION_KEY_BIT 进行调整和测试
如上述,我们知道了 Db2 数据库在创建伊始就决定了排序方式,后续是无法更改的,但是有一个变通的方法是使用 COLLATION_KEY_BIT 函数可以显式指定一种 collation,这里可以指定 Unicode Collation Algorithm-based collations 或者 language-aware collations for Unicode data,它可以在任何一种数据库中操作,因此可以方便地进行验证和测试,这里举几个例子:
- 我们在一个 using codeset GBK territory CN 的数据库中进行操作,这显然不是一个 UCA 算法的数据库,正如第一小节所描述的,正常情况下,排序和比较应该是这样的:
db2 "select * from test1 order by c1"
C1
----------
china
China
j2
j6
J2
J6
6 record(s) selected.
db2 "select * from sysibm.sysdummy1 where 'a'='A'"
IBMREQD
-------
0 record(s) selected.
- 使用 S1 不区分重音和大小写,使用 EX 不区分大小写,结果是不区分大小写:
db2 "select * from sysibm.sysdummy1 where COLLATION_KEY_BIT('a','CLDR2701_LZH_KUNIHAN_S1_EX')=COLLATION_KEY_BIT('A','CLDR2701_LZH_KUNIHAN_S1_EX')"
IBMREQD
-------
Y
1 record(s) selected.
- 用 S1 不区分重音和大小写,使用 EO 区分大小写,结果是区分大小写:
db2 "select * from sysibm.sysdummy1 where COLLATION_KEY_BIT('a','CLDR2701_LZH_KUNIHAN_S1_EO')=COLLATION_KEY_BIT('A','CLDR2701_LZH_KUNIHAN_S1_EO')"
IBMREQD
-------
0 record(s) selected.
- S2 和 S1 的差别仅在重音,大小写方面没有差别,测试结果也完全一样:
db2 "select * from sysibm.sysdummy1 where COLLATION_KEY_BIT('a','CLDR2701_LZH_KUNIHAN_S2_EX')=COLLATION_KEY_BIT('A','CLDR2701_LZH_KUNIHAN_S2_EX')"
IBMREQD
-------
Y
1 record(s) selected.
db2 "select * from sysibm.sysdummy1 where COLLATION_KEY_BIT('a','CLDR2701_LZH_KUNIHAN_S2_EO')=COLLATION_KEY_BIT('A','CLDR2701_LZH_KUNIHAN_S2_EO')"
IBMREQD
-------
0 record(s) selected.
- 使用 S3 区分重音和大小写,使用 EX 不区分大小写,结果是区分大小写:
db2 "select * from sysibm.sysdummy1 where COLLATION_KEY_BIT('a','CLDR2701_LZH_KUNIHAN_S3_EX')=COLLATION_KEY_BIT('A','CLDR2701_LZH_KUNIHAN_S3_EX')"
IBMREQD
-------
0 record(s) selected.
- 使用 S3 区分重音和大小写,使用 EO 区分大小写,结果是区分大小写:
db2 "select * from sysibm.sysdummy1 where COLLATION_KEY_BIT('a','CLDR2701_LZH_KUNIHAN_S3_EO')=COLLATION_KEY_BIT('A','CLDR2701_LZH_KUNIHAN_S3_EO')"
IBMREQD
-------
0 record(s) selected.
- 使用 CU 大写优先,注意这里依然是多级比较:
db2 "select * from test1 order by COLLATION_KEY_BIT(c1,'CLDR2701_LZH_KUNIHAN_CU')"
C1
----------
China
china
J2
j2
J6
j6
6 record(s) selected.
- 使用 CL 小写优先,注意这里依然是多级比较:
db2 "select * from test1 order by COLLATION_KEY_BIT(c1,'CLDR2701_LZH_KUNIHAN_CL')"
C1
----------
china
China
j2
J2
j6
J6
6 record(s) selected.
- 不管是 CU 还是 CL,至少要开启区分大小写,如果都关闭((S1 或 S2) 且 EX),那么对于相同的字符串,大写或是小写在前面都是合理的,会按照输入(INSERT)的顺序排序输出:
db2 "select * from test1 order by COLLATION_KEY_BIT(c1,'CLDR2701_LZH_KUNIHAN_S1_EX_CU')"
C1
----------
china
China
J2
j2
j6
J6
6 record(s) selected.
db2 "select * from test1 order by COLLATION_KEY_BIT(c1,'CLDR2701_LZH_KUNIHAN_S2_EX_CU')"
C1
----------
china
China
J2
j2
j6
J6
6 record(s) selected.
Db2 和 MySQL 关于 collation 的比较
总结来说:
- Db2 的 UTF-8 identity 和 GBK identity 分别等价于 MySQL 的 utf8mb4_bin 和 gbk_bin
- Db2 非 Unicode 的 system 映射到 language-aware 的 collation 无法在 MySQL 中找到等价的 collation
- Db2 Unicode 的 UCA 中的 KUNIHAN_S1_EX 或 KUNIHAN_S2_EX 等价于 MySQL 的 utf8mb4_unicode_ci,但需要注意这里忽略大小写,所以不同大小写的相同字符串会按照输入顺序来排序输出。
- MySQL 的 gb18030_chinese_ci 或 gbk_chinese_ci 在算法上也是 UCA 的多级比较(优先看整个字符串,再考虑大小写),所以 Db2 Unicode 的 UCA 中的 KGB2312HAN_S1_EX 或 KGB2312HAN_S2_EX 等价于 MySQL 的 gb18030_chinese_ci 或 gbk_chinese_ci,但需要注意这里忽略大小写,所以不同大小写的相同字符串会按照输入顺序来排序输出。
- Db2 UCA 中的 CLDR2701_LZH_KGB2312HAN 和 CLDR2701_LZH 经测试可以根据拼音排序中文字符
- Db2 UCA 中的 CLDR2701_LZH_KSTROKE 经测试可以根据笔画多少排序
附录
- Supported territory codes and code pages
- Code page 1386, Generic (SYSTEM_1386)
- Code set and collation combinations
- Unicode Collation Algorithm based collations
- UNICODE COLLATION ALGORITHM
- Unicode 字符串排序规则(二):如何比较字符串
- Unicode 归类算法 (UCA)
- 10.10.1 Unicode Character Sets
- CREATE DATABASE command
10.Choosing a collation for a Unicode database