24.2. 排序规则支持 #
排序规则特性允许为每一列甚至每一次操作指定数据的排序顺序和字符分类行为。这缓解了数据库的 LC_COLLATE 和 LC_CTYPE
设置在创建之后无法更改这一限制。
24.2.1. 概念 #
从概念上讲,每个支持排序规则的数据类型的表达式都有一个排序规则。(内置的支持排序规则的数据类型包括 text、varchar 和 char。用户定义的基础类型也可以标记为支持排序规则,当然,建立在支持排序规则的数据类型之上的域也支持排序规则。)如果表达式是列引用,则该表达式的排序规则就是该列定义的排序规则。如果表达式是常量,则其排序规则就是该常量数据类型的默认排序规则。更复杂表达式的排序规则则按下文所述,从其输入表达式的排序规则推导出来。
表达式的排序规则可以是“默认”排序规则,这表示为数据库定义的区域设置。表达式的排序规则也可能是不确定的。在这种情况下,排序操作以及其他需要知道排序规则的操作都会失败。
当数据库系统必须执行排序或字符分类时,它会使用输入表达式的排序规则。例如,这会发生在 ORDER BY 子句以及函数或操作符调用(如
<)中。应用于 ORDER BY 子句的排序规则就是排序键的排序规则。应用于函数或操作符调用的排序规则则按下文所述,从参数中推导出来。除了比较操作符之外,在大小写之间进行转换的函数(例如 lower、upper 和
initcap)、模式匹配操作符,以及 to_char
及相关函数,也都会考虑排序规则。
对于函数或操作符调用,通过检查参数排序规则推导出的排序规则,会在运行时用于执行指定操作。如果该函数或操作符调用的结果属于支持排序规则的数据类型,那么在解析时它也会被用作该函数或操作符表达式的已定义排序规则,以便在外围表达式需要知道其排序规则时使用。
表达式的排序规则派生可以是显式的,也可以是隐式的。这一区别会影响当一个表达式中出现多个不同排序规则时,系统如何把它们组合起来。使用 COLLATE 子句时,会发生显式排序规则派生;其他所有排序规则派生都是隐式的。当需要组合多个排序规则时,例如在函数调用中,将使用以下规则:
如果任一输入表达式具有显式排序规则派生,那么输入表达式中所有显式派生的排序规则都必须相同,否则会报错。如果存在显式派生的排序规则,那么排序规则组合的结果就是该排序规则。
否则,所有输入表达式都必须具有相同的隐式排序规则派生,或者为默认排序规则。如果存在任何非默认排序规则,那么排序规则组合的结果就是该排序规则;否则,结果就是默认排序规则。
如果输入表达式之间存在相互冲突的非默认隐式排序规则,则该组合会被视为具有不确定排序规则。除非被调用的特定函数确实需要知道它应当使用哪个排序规则,否则这并不是错误。如果它确实需要,就会在运行时抛出错误。
例如,考虑如下表定义:
CREATE TABLE test1 (
a text COLLATE "de_DE",
b text COLLATE "es_ES",
...
);
那么在
SELECT a < 'foo' FROM test1;
中,<比较会按 de_DE 规则进行,因为该表达式组合了一个隐式派生的排序规则和默认排序规则。但在
SELECT a < ('foo' COLLATE "fr_FR") FROM test1;
中,比较会按 fr_FR 规则进行,因为显式排序规则派生覆盖了隐式派生。进一步,给定
SELECT a < b FROM test1;
解析器无法确定应当应用哪个排序规则,因为 a 列和 b 列具有冲突的隐式排序规则。由于
<操作符确实需要知道要使用哪个排序规则,因此这会导致一个错误。该错误可以通过给任一输入表达式附加显式排序规则说明符来解决,例如:
SELECT a < b COLLATE "de_DE" FROM test1;
或者等价地:
SELECT a COLLATE "de_DE" < b FROM test1;
另一方面,结构相似的情况
SELECT a || b FROM test1;
不会导致错误,因为||操作符不关心排序规则:无论使用什么排序规则,其结果都相同。
如果函数或操作符返回的是支持排序规则的数据类型,那么分配给该函数或操作符的输入表达式组合的排序规则,也被认为适用于其结果。因此,在
SELECT * FROM test1 ORDER BY a || 'foo';
中,排序将按 de_DE 规则进行。但这个查询:
SELECT * FROM test1 ORDER BY a || b;
会报错,因为即使||操作符本身不需要知道排序规则,ORDER BY 子句仍然需要。与之前一样,可以通过显式排序规则说明符来解决冲突:
SELECT * FROM test1 ORDER BY a || b COLLATE "fr_FR";
24.2.2. 管理排序规则 #
排序规则是一个 SQL 模式对象,它把某个 SQL 名称映射到操作系统中已安装库提供的区域设置。排序规则定义具有一个提供程序,用于指定由哪个库提供区域设置数据。标准提供程序之一是 libc,它使用操作系统 C 库提供的区域设置。这些也是大多数操作系统工具所使用的区域设置。另一个提供程序是 icu,它使用外部
ICU 库。只有在构建
PostgreSQL 时配置了 ICU 支持,才能使用 ICU
区域设置。
由 libc 提供的排序规则对象,对应于
LC_COLLATE 和 LC_CTYPE 设置的组合,其格式与系统库调用 setlocale() 所接受的格式相同。(顾名思义,排序规则的主要用途是设置控制排序顺序的
LC_COLLATE。但在实践中,很少有必要让
LC_CTYPE 和 LC_COLLATE 不同,因此把二者归为一个概念,比再建立一套为每个表达式设置 LC_CTYPE
的机制更方便。)此外,libc 排序规则还与某种字符集编码绑定(见第 24.3 节)。同一个排序规则名称可能会在不同编码中出现。
由 icu 提供的排序规则对象,对应于 ICU 库提供的具名排序器。ICU 不支持将“collate”和“ctype”分开设置,因此二者总是相同的。此外,ICU 排序规则与编码无关,因此在一个数据库中,某个给定名称的 ICU 排序规则始终只有一个。
24.2.2.1. 标准排序规则 #
所有平台都提供名为 default、C 和 POSIX 的排序规则。根据操作系统支持情况,还可能提供其他排序规则。default 排序规则选择创建数据库时指定的 LC_COLLATE 和 LC_CTYPE 值。C 和 POSIX 排序规则都采用“传统 C”行为,仅将 ASCII 字母“A”到“Z”视为字母,并严格按字符编码的字节值排序。
注意
C 和 POSIX 区域设置的行为可能因数据库编码而异。
此外,还可以使用两个 SQL 标准排序规则名:
unicode此排序规则使用 Unicode 排序算法和默认 Unicode 排序元素表进行排序。它在所有编码中都可用。使用此排序规则需要 ICU 支持。(此排序规则与 ICU 根区域设置具有相同的行为;参见
und-x-icu(表示“未定义”)。)ucs_basic此排序规则按 Unicode 代码点排序。仅适用于
UTF8编码。(此排序规则与UTF8编码下 libc 区域设置规范C的行为相同。)
24.2.2.2. 预定义排序规则 #
如果操作系统支持在单个程序中使用多个区域设置(newlocale
及相关函数),或者配置了 ICU 支持,那么在初始化数据库集簇时,initdb 会根据当时在操作系统中发现的所有区域设置,用排序规则填充系统目录 pg_collation。
要检查当前可用的区域设置,可使用查询 SELECT * FROM pg_collation,或在 psql 中使用命令\dOS+。
24.2.2.2.1. libc 排序规则 #
例如,操作系统可能提供一个名为 de_DE.utf8 的区域设置。initdb 随后会为 UTF8 编码创建一个名为 de_DE.utf8 的排序规则,其
LC_COLLATE 和 LC_CTYPE 都设置为
de_DE.utf8。它还会再创建一个从名称中去掉
.utf8 标签的排序规则。因此,你也可以使用
de_DE 这个名称来使用该排序规则,这样写起来更方便,且名称与编码的耦合更小。不过请注意,初始排序规则名称集合仍然取决于平台。
由 libc 提供的默认排序规则,直接映射到操作系统中已安装的区域设置,它们可以用 locale -a 命令列出。如果需要某个 libc 排序规则,而其 LC_COLLATE
和 LC_CTYPE 取值不同,或者数据库系统初始化后又在操作系统中安装了新的区域设置,那么可以使用 CREATE COLLATION 命令创建新的排序规则。新的操作系统区域设置也可以用 pg_import_system_collations()
函数批量导入。
在任何特定数据库中,只有使用该数据库编码的排序规则才有意义。pg_collation 中的其他条目会被忽略。因此,像
de_DE 这样去掉编码后缀的排序规则名称,在某个给定数据库内可以视为唯一,即使它在全局范围内并不唯一。推荐使用这种去掉后缀的排序规则名称,因为如果你决定改用另一种数据库编码,需要改动的地方会更少。不过要注意,default、C 和 POSIX
排序规则不受数据库编码影响,始终都可以使用。
PostgreSQL 即使面对具有相同属性的不同排序规则对象,也会把它们视为不兼容。例如:
SELECT a COLLATE "C" < b COLLATE "POSIX" FROM test1;
即使 C 和 POSIX 排序规则的行为完全相同,这仍然会报错。因此,不建议混用去掉后缀和保留后缀的排序规则名称。
24.2.2.2.2. ICU 排序规则 #
对于 ICU,枚举所有可能的区域设置名称并不合理。ICU 为区域设置使用特定的命名系统,但给区域设置命名的方法远多于实际存在的不同区域设置。initdb 使用 ICU API 提取一组不同的区域设置,以填充初始排序规则集合。由 ICU 提供的排序规则在 SQL 环境中创建时,其名称采用 BCP 47 语言标签格式,并附加“私有使用”扩展
-x-icu,以便与 libc 区域设置区分开来。
下面是一些可能创建出来的排序规则示例:
某些(较少使用的)编码不受 ICU 支持。当数据库编码属于其中之一时,pg_collation 中的 ICU 排序规则条目会被忽略。尝试使用这样的排序规则会报出类似“collation "de-x-icu" for encoding
"WIN874" does not exist”的错误。
24.2.2.3. 创建新的排序规则对象 #
如果标准和预定义排序规则不足以满足需求,用户可以使用 SQL 命令 CREATE COLLATION 创建自己的排序规则对象。
与所有预定义对象一样,标准和预定义排序规则都位于模式
pg_catalog 中。用户定义的排序规则应当创建在用户模式中。这也能确保它们被 pg_dump 保存。
24.2.2.3.1. libc 排序规则 #
新的 libc 排序规则可以这样创建:
CREATE COLLATION german (provider = libc, locale = 'de_DE');
该命令中 locale 子句可接受的确切值取决于操作系统。在类 Unix 系统上,命令 locale -a 会给出一个列表。
由于预定义的 libc 排序规则已经包含了数据库实例初始化时操作系统中定义的所有排序规则,因此通常不需要手工创建新的排序规则。可能需要这样做的原因包括希望采用不同的命名系统(这种情况下另见第 24.2.2.3.3 节),或操作系统升级后提供了新的区域设置定义(这种情况下另见 pg_import_system_collations())。
24.2.2.3.2. ICU 排序规则 #
ICU 排序规则可以这样创建:
CREATE COLLATION german (provider = icu, locale = 'de-DE');
ICU 区域设置使用 BCP 47 语言标签指定,但也接受大多数 libc 风格的区域设置名称。如果可能,libc 风格的区域设置名称会被转换为语言标签。
新的 ICU 排序规则可以通过在语言标签中包含排序属性来广泛定制排序行为。详细信息和示例见第 24.2.3 节。
24.2.2.3.3. 复制排序规则 #
命令 CREATE COLLATION 也可以用来从现有排序规则创建一个新的排序规则。这有助于在应用程序中使用与操作系统无关的排序规则名称、创建兼容性名称,或者以更易读的名称来使用 ICU 提供的排序规则。例如:
CREATE COLLATION german FROM "de_DE"; CREATE COLLATION french FROM "fr-x-icu";
24.2.2.4. 非确定性排序规则 #
排序规则要么是确定性的,要么是非确定性的。确定性排序规则使用确定性比较,这意味着只有当两个字符串由完全相同的字节序列组成时,才会认为它们相等。非确定性比较则可能在两个字符串的字节不同的情况下,仍判断它们相等。典型情况包括不区分大小写的比较、不区分重音的比较,以及对采用不同 Unicode
规范化形式的字符串进行比较。具体是否实现这些不敏感比较,取决于排序规则提供程序;deterministic 标志只决定当比较结果相等时,是否再使用按字节比较来打破平局。更多术语信息可参见 Unicode 技术标准 10。
要创建非确定性排序规则,可在 CREATE COLLATION 中指定属性 deterministic = false,例如:
CREATE COLLATION ndcoll (provider = icu, locale = 'und', deterministic = false);
这个示例会以非确定性方式使用标准 Unicode 排序规则。特别地,它允许不同规范化形式的字符串被正确比较。更有意思的示例则会用到上文介绍的 ICU 定制功能。例如:
CREATE COLLATION case_insensitive (provider = icu, locale = 'und-u-ks-level2', deterministic = false); CREATE COLLATION ignore_accents (provider = icu, locale = 'und-u-ks-level1-kc-true', deterministic = false);
所有标准和预定义排序规则都是确定性的,所有用户定义排序规则默认也都是确定性的。虽然非确定性排序规则提供了更“正确”的行为,尤其是在考虑 Unicode 的全部能力及其众多特殊情况时,但它们也有一些缺点。首先,使用它们会带来性能损失。特别要注意的是,B-树不能对使用非确定性排序规则的索引使用去重。此外,某些操作(例如模式匹配操作)对非确定性排序规则来说是不可行的。因此,只有在确实需要时才应使用它们。
提示
若要处理采用不同 Unicode 规范化形式的文本,另一种办法是使用
normalize 函数和 is normalized
表达式预处理或检查字符串,而不是使用非确定性排序规则。每种做法都有不同的权衡。
24.2.3. ICU 自定义排序规则 #
ICU 允许通过把排序设置作为语言标签的一部分,定义新的排序规则,从而对排序规则行为进行广泛控制。这些设置可以修改排序顺序,以适应各种需求。例如:
-- ignore differences in accents and case CREATE COLLATION ignore_accent_case (provider = icu, deterministic = false, locale = 'und-u-ks-level1'); SELECT 'Å' = 'A' COLLATE ignore_accent_case; -- true SELECT 'z' = 'Z' COLLATE ignore_accent_case; -- true -- upper case letters sort before lower case. CREATE COLLATION upper_first (provider = icu, locale = 'und-u-kf-upper'); SELECT 'B' < 'b' COLLATE upper_first; -- true -- treat digits numerically and ignore punctuation CREATE COLLATION num_ignore_punct (provider = icu, deterministic = false, locale = 'und-u-ka-shifted-kn'); SELECT 'id-45' < 'id-123' COLLATE num_ignore_punct; -- true SELECT 'w;x*y-z' = 'wxyz' COLLATE num_ignore_punct; -- true
许多可用选项会在第 24.2.3.2 节中说明;更详尽的细节则可参见第 24.2.3.5 节。
24.2.3.1. ICU 比较级别 #
ICU 中两个字符串的比较(排序)由一个多级过程决定,其中各种文本特征被分组为“级别”。每个级别的处理由排序规则设置控制。级别越高,对文本特征的区分就越细。
表 24.1 显示了在给定级别判断相等性时,哪些文本特征差异会被认为是重要的。Unicode 字符 U+2063
是不可见分隔符,如表中所示,在低于 identic 的所有比较级别都会被忽略。
表 24.1. ICU 排序规则级别
| 级别 | 描述 | 'f' = 'f' | 'ab' = U&'a\2063b' | 'x-y' = 'x_y' | 'g' = 'G' | 'n' = 'ñ' | 'y' = 'z' |
|---|---|---|---|---|---|---|---|
| level1 | 基本字符 | true | true | true | true | true | false |
| level2 | 重音 | true | true | true | true | false | false |
| level3 | 大小写/变体 | true | true | true | false | false | false |
| level4 | 标点符号 | true | true | false | false | false | false |
| identic | 全部 | true | false | false | false | false | false |
在每个级别上,即使关闭了完全规范化,也会执行基本规范化。例如,'á' 既可能由代码点序列
U&'\0061\0301' 表示,也可能由单个代码点
U&'\00E1' 表示;即使在 identic
级别,这两种表示也会被视为相等。若要把代码点表示上的任何差异都视为不同,请使用将 deterministic 设为 true 创建的排序规则。
24.2.3.1.1. 排序规则级别示例 #
CREATE COLLATION level3 (provider = icu, deterministic = false, locale = 'und-u-ka-shifted-ks-level3'); CREATE COLLATION level4 (provider = icu, deterministic = false, locale = 'und-u-ka-shifted-ks-level4'); CREATE COLLATION identic (provider = icu, deterministic = false, locale = 'und-u-ka-shifted-ks-identic'); -- invisible separator ignored at all levels except identic SELECT 'ab' = U&'a\2063b' COLLATE level4; -- true SELECT 'ab' = U&'a\2063b' COLLATE identic; -- false -- punctuation ignored at level3 but not at level 4 SELECT 'x-y' = 'x_y' COLLATE level3; -- true SELECT 'x-y' = 'x_y' COLLATE level4; -- false
24.2.3.2. ICU 区域设置的排序规则设置 #
表 24.2 显示了可用的排序规则设置,这些设置可以作为语言标签的一部分,用来定制排序规则。
表 24.2. ICU 排序规则设置
| 键 | 值 | 默认 | 描述 |
|---|---|---|---|
co | emoji, phonebk, standard, ... | standard | 排序规则类型。有关其他选项和详细信息,请参见第 24.2.3.5 节。 |
ka | noignore, shifted | noignore |
如果设为 shifted,某些字符(例如标点或空格)在比较时会被忽略。要使其生效,键 ks 必须设为
level3 或更低。可通过设置键 kv
来控制忽略哪些字符类别。
|
kb | true, false | false |
对 2 级差异进行反向比较。例如,区域设置 und-u-kb
会把'àe' 排在'aé' 之前。
|
kc | true, false | false |
将大小写分离成一个介于重音和其他 3 级特征之间的“2.5 级”。
如果其值为 |
kf |
upper, lower,
false
| false |
若设为 upper,大写字母排在小写字母之前;若设为
lower,小写字母排在大写字母之前;若设为
false,排序顺序则取决于该区域设置的规则。
|
kn | true, false | false |
如果设为 true,字符串中的数字会被视为单个数值,而不是一串数字。例如,'id-45' 会排在
'id-123' 之前。
|
kk | true, false | false |
启用完全规范化;这可能影响性能。即使其值为
在某些情况下,完全规范化很重要,例如多个重音符应用于同一个字符时。比如,代码点序列 |
kr |
space, punct,
symbol, currency,
digit, script-id
|
可设为一个或多个有效值,或者任何 BCP 47
它会重新定义字符类别的排序顺序;列表中较早出现的字符类别,会排在列表中较晚类别的前面。例如,值
| |
ks | level1, level2, level3, level4, identic | level3 |
用于确定相等性时的敏感度(或“强度”),其中
level1 对差异最不敏感,而
identic 对差异最敏感。详见表 24.1。
|
kv |
space, punct,
symbol, currency
| punct |
在 3 级比较时会被忽略的字符类别。设为更靠后的值时也会包含更靠前的值;例如 symbol 也会把 punct
和 space 包含在要忽略的字符中。要使其生效,键
ka 必须设为 shifted,且键
ks 必须设为 level3 或更低。
|
默认值可能依赖于具体区域设置。上表并非意在穷举所有情况。有关其他选项和详细信息,请参见第 24.2.3.5 节。
注意
对于许多排序规则设置,必须在创建排序规则时把
deterministic 设为 false,这些设置才能产生预期效果(参见第 24.2.2.4 节)。此外,某些设置只有在键
ka 设为 shifted 时才会生效(参见表 24.2)。
24.2.3.3. 排序规则设置示例 #
CREATE COLLATION "de-u-co-phonebk-x-icu" (provider = icu, locale = 'de-u-co-phonebk');#德语排序规则,电话簿排序类型
CREATE COLLATION "und-u-co-emoji-x-icu" (provider = icu, locale = 'und-u-co-emoji');#根排序规则,表情符号排序类型,依据 Unicode 技术标准 #51
CREATE COLLATION latinlast (provider = icu, locale = 'en-u-kr-grek-latn');#将希腊字母排在拉丁字母之前。(默认情况下是拉丁字母排在希腊字母之前。)
CREATE COLLATION upperfirst (provider = icu, locale = 'en-u-kf-upper');#将大写字母排在小写字母之前。(默认情况下是小写字母在前。)
CREATE COLLATION special (provider = icu, locale = 'en-u-kf-upper-kr-grek-latn');#结合了以上两个选项。
24.2.3.4. ICU 定制规则 #
如果上面列出的排序规则设置所提供的选项仍然不够用,可以用定制规则改变排序元素的顺序,其语法详见 https://unicode-org.github.io/icu/userguide/collation/customization/。
下面这个小例子基于根区域设置创建了一个带定制规则的排序规则:
CREATE COLLATION custom (provider = icu, locale = 'und', rules = '&V << w <<< W');
按照这条规则,字母“W”会排在“V”之后,但被视为类似重音那样的次要差异。某些语言的区域设置定义中就包含类似这样的规则。(当然,如果某个区域设置定义本身已经包含了所需规则,就无需再次显式指定。)
下面是一个更复杂的例子。以下语句设置了一个名为 ebcdic
的排序规则,其规则会按照 EBCDIC 编码的顺序对 US-ASCII 字符进行排序。
CREATE COLLATION ebcdic (provider = icu, locale = 'und',
rules = $$
& ' ' < '.' < '<' < '(' < '+' < \|
< '&' < '!' < '$' < '*' < ')' < ';'
< '-' < '/' < ',' < '%' < '_' < '>' < '?'
< '`' < ':' < '#' < '@' < \' < '=' < '"'
<*a-r < '~' <*s-z < '^' < '[' < ']'
< '{' <*A-I < '}' <*J-R < '\' <*S-Z <*0-9
$$);
SELECT c
FROM (VALUES ('a'), ('b'), ('A'), ('B'), ('1'), ('2'), ('!'), ('^')) AS x(c)
ORDER BY c COLLATE ebcdic;
c
---
!
a
b
^
A
B
1
2
24.2.3.5. ICU 的外部参考 #
本节(第 24.2.3 节)只是对 ICU 行为和语言标签的简要概述。有关技术细节、其他选项和新的行为,请参阅以下文档:
报告文档问题
阅读 上游文档. 通过 PostgreSQL 文档反馈表单.