受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

23.2. 排序规则支持 #

排序规则特性允许为每一列甚至每一次操作指定数据的排序顺序和字符分类行为。 这缓解了数据库的LC_COLLATELC_CTYPE 设置在创建之后无法更改这一限制。

23.2.1. 概念 #

从概念上讲,每个可排序数据类型的表达式都有一个排序规则。(内置的可排序 数据类型包括textvarcharchar。 用户定义的基本类型也可以标记为可排序,当然,建立在可排序数据类型之上的 域也是可排序的。) 如果表达式是列引用,则该表达式的排序规则就是该列定义的排序规则。如果表 达式是常量,则其排序规则就是该常量数据类型的默认排序规则。更复杂表达式 的排序规则则按下文所述,从其输入表达式的排序规则推导出来。

表达式的排序规则可以是默认排序规则,这表示为数据库定义的 区域设置。表达式的排序规则也可能是不确定的。在这种情况下,排序操作以及 其他需要知道排序规则的操作都会失败。

当数据库系统必须执行排序或字符分类时,它会使用输入表达式的排序规则。这 例如发生在ORDER BY子句以及函数或操作符调用(如 <)中。应用于ORDER BY子句的 排序规则就是排序键的排序规则。应用于函数或操作符调用的排序规则则按下文 所述,从参数中推导出来。除了比较操作符之外,在大小写之间进行转换的函数 (例如lowerupperinitcap)、模式匹配操作符,以及to_char 及相关函数,也都会考虑排序规则。

对于函数或操作符调用,通过检查参数排序规则推导出的排序规则,会在运行时 用于执行指定操作。如果该函数或操作符调用的结果属于可排序数据类型,那么 在解析时它也会被用作该函数或操作符表达式的已定义排序规则,以便在外围表 达式需要知道其排序规则时使用。

表达式的排序规则派生可以是显式的,也可以是隐式 的。这一区别会影响当一个表达式中出现多个不同排序规则时,系统如何把它们 组合起来。使用COLLATE子句时,会发生显式排序规则派 生;其他所有排序规则派生都是隐式的。当需要组合多个排序规则时,例如在函 数调用中,将使用以下规则:

  1. 如果任一输入表达式具有显式排序规则派生,那么输入表达式中所有显式派生 的排序规则都必须相同,否则会报错。如果存在显式派生的排序规则,那么排 序规则组合的结果就是该排序规则。

  2. 否则,所有输入表达式都必须具有相同的隐式排序规则派生,或者为默认排序 规则。如果存在任何非默认排序规则,那么排序规则组合的结果就是该排序规 则;否则,结果就是默认排序规则。

  3. 如果输入表达式之间存在相互冲突的非默认隐式排序规则,则该组合会被视为 具有不确定排序规则。除非被调用的特定函数确实需要知道它应当使用哪个排 序规则,否则这并不是错误。如果它确实需要,就会在运行时抛出错误。

例如,考虑如下表定义:

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";

23.2.2. 管理排序规则 #

排序规则是一个 SQL 模式对象,它把某个 SQL 名称映射到操作系统中已安装库 提供的区域设置。排序规则定义具有一个提供程序, 用于指定由哪个库提供区域设置数据。标准提供程序之一是libc, 它使用操作系统 C 库提供的区域设置。这些也是大多数操作系统工具所使用的 区域设置。另一个提供程序是icu,它使用外部 ICU库。只有在构建 PostgreSQL时配置了 ICU 支持,才能使用 ICU 区域设置。

libc提供的排序规则对象,对应于 LC_COLLATELC_CTYPE设置的组合, 其格式与系统库调用setlocale()所接受的格式相同。 (顾名思义,排序规则的主要用途是设置控制排序顺序的 LC_COLLATE。但在实践中,很少有必要让 LC_CTYPELC_COLLATE不同,因此把 二者归为一个概念,比再建立一套为每个表达式设置LC_CTYPE 的机制更方便。)此外,libc排序规则还与某种字符集编 码绑定(见Section 23.3)。同一个排序规则名称可能会在不 同编码中出现。

icu提供的排序规则对象,对应于 ICU 库提供的具名整 理器。ICU 不支持将collatectype分开设 置,因此二者总是相同的。此外,ICU 排序规则与编码无关,因此在一个数据库 中,某个给定名称的 ICU 排序规则始终只有一个。

23.2.2.1. 标准排序规则 #

在所有平台上,都支持以下排序规则:

unicode

此 SQL 标准排序规则使用 Unicode 排序算法和默认 Unicode 排序元素表进 行排序。它在所有编码中都可用。 使用此排序规则需要 ICU 支持,并且如果 PostgreSQL使用不同版本的 ICU 构建,其 行为可能会发生变化。(此排序规则与 ICU 根区域设置具有相同的行为;参 见und-x-icu(表示未定义。)

ucs_basic

此 SQL 标准排序规则使用 Unicode 代码点值而不是自然语言顺序进行排 序,并且只有 ASCII 字母AZ会被视为字母。其行为高效且在所 有版本中都稳定。仅适用于UTF8编码。(此排序规则 与UTF8编码下 libc 区域设置规范C 的行为相同。)

pg_unicode_fast

此排序规则按 Unicode 代码点值而不是自然语言顺序进行排序。对于 lowerinitcapupper函数,它使用 Unicode 完整大小写映射。对 于模式匹配(包括正则表达式),它使用 Unicode 兼容属性 的标准变体。其行为在 Postgres主版本内高效且稳定。仅适用于 UTF8编码。

pg_c_utf8

此排序规则按 Unicode 代码点值而不是自然语言顺序进行排序。对于 lowerinitcapupper函数,它使用 Unicode 简单大小写映射。对 于模式匹配(包括正则表达式),它使用 Unicode 兼容属性 的 POSIX 兼容变体。其行为在 PostgreSQL主版本内高效且稳定。此排序规 则仅适用于UTF8编码。

C(等同于POSIX

CPOSIX排序规则基于 传统 C行为。它们按字节值而不是自然语言顺序排序,并且 只有 ASCII 字母AZ会被视为字母。对于给定的数据库 编码,其行为高效且在所有版本中都稳定,但在不同数据库编码之间可能会 有所不同。

default

default排序规则会选择数据库创建时指定的区域设 置。

根据操作系统支持情况,还可能存在其他排序规则。这些附加排序规则的效率和 稳定性取决于排序规则提供程序、提供程序版本以及区域设置。

23.2.2.2. 预定义排序规则 #

如果操作系统支持在单个程序中使用多个区域设置(newlocale 及相关函数),或者配置了 ICU 支持,那么在初始化数据库集簇时, initdb会根据当时在操作系统中发现的所有区域设置,用 排序规则填充系统目录pg_collation

要检查当前可用的区域设置,可使用查询SELECT * FROM pg_collation, 或在psql中使用命令\dOS+

23.2.2.2.1. libc 排序规则 #

例如,操作系统可能提供一个名为de_DE.utf8的区域设 置。initdb随后会为UTF8编码创建 一个名为de_DE.utf8的排序规则,其 LC_COLLATELC_CTYPE都设置为 de_DE.utf8。它还会再创建一个从名称中去掉 .utf8标签的排序规则。因此,你也可以使用 de_DE这个名称来使用该排序规则,这样写起来更方便, 且名称与编码的耦合更小。不过请注意,初始排序规则名称集合仍然取决于平 台。

libc提供的默认排序规则,直接映射到操作系统中已安 装的区域设置,它们可以用locale -a命令列出。如果需 要某个libc排序规则,而其LC_COLLATELC_CTYPE取值不同,或者数据库系统初始化后又在操作系 统中安装了新的区域设置,那么可以使用CREATE COLLATION 命令创建新的排序规则。新的操作系统区域设置也可以用pg_import_system_collations() 函数批量导入。

在任何特定数据库中,只有使用该数据库编码的排序规则才有意义。 pg_collation中的其他条目会被忽略。因此,像 de_DE这样去掉编码后缀的排序规则名称,在某个给定数 据库内可以视为唯一,即使它在全局范围内并不唯一。推荐使用这种去掉后缀 的排序规则名称,因为如果你决定改用另一种数据库编码,需要改动的地方会 更少。不过要注意, defaultCPOSIX 排序规则不受数据库编码影响,始终都可以使用。

PostgreSQL即使面对具有相同属性的不同排序规 则对象,也会把它们视为不兼容。例如:

SELECT a COLLATE "C" < b COLLATE "POSIX" FROM test1;

即使CPOSIX排序规则的行为完全 相同,这仍然会报错。因此,不建议混用去掉后缀和保留后缀的排序规则名 称。

23.2.2.2.2. ICU 排序规则 #

对于 ICU,枚举所有可能的区域设置名称并不合理。ICU 为区域设置使用特定 的命名系统,但给区域设置命名的方法远多于实际存在的不同区域设置。 initdb使用 ICU API 提取一组不同的区域设置,以填充 初始排序规则集合。由 ICU 提供的排序规则在 SQL 环境中创建时,其名称采 用 BCP 47 语言标签格式,并附加私有使用扩展 -x-icu,以便与 libc 区域设置区分开来。

下面是一些可能创建出来的排序规则示例:

de-x-icu #

德语排序规则,默认变体

de-AT-x-icu #

奥地利德语排序规则,默认变体

(还有例如de-DE-x-icude-CH-x-icu, 但在撰写本文时,它们与de-x-icu等价。)

und-x-icu(表示未定义 #

ICUroot排序规则。可用它获得一种较为合理、与特定语 言无关的排序顺序。

某些(较少使用的)编码不受 ICU 支持。当数据库编码属于其中之一时, pg_collation中的 ICU 排序规则条目会被忽略。尝试使 用这样的排序规则会报出类似collation "de-x-icu" for encoding "WIN874" does not exist的错误。

23.2.2.3. 创建新的排序规则对象 #

如果标准和预定义排序规则不足以满足需求,用户可以使用 SQL 命令CREATE COLLATION创建自己的排序规则对象。

与所有预定义对象一样,标准和预定义排序规则都位于模式 pg_catalog中。用户定义的排序规则应当创建在用户模式 中。这也能确保它们被pg_dump保存。

23.2.2.3.1. libc 排序规则 #

新的 libc 排序规则可以这样创建:

CREATE COLLATION german (provider = libc, locale = 'de_DE');

该命令中locale子句可接受的确切值取决于操作系统。在 类 Unix 系统上,命令locale -a会给出一个列表。

由于预定义的 libc 排序规则已经包含了数据库实例初始化时操作系统中定义 的所有排序规则,因此通常不需要手工创建新的排序规则。可能需要这样做的 原因包括希望采用不同的命名系统(这种情况下另见Section 23.2.2.3.3),或操作系统升级后提供了新的区域设置定义 (这种情况下另见pg_import_system_collations())。

23.2.2.3.2. ICU 排序规则 #

ICU 排序规则可以这样创建:

CREATE COLLATION german (provider = icu, locale = 'de-DE');

ICU 区域设置使用 BCP 47 语言标签 指定,但也接受大多数 libc 风格的区域设置名称。如果可能,libc 风格的区 域设置名称会被转换为语言标签。

新的 ICU 排序规则可以通过在语言标签中包含排序属性来广泛定制排序行为。 详细信息和示例见Section 23.2.3

23.2.2.3.3. 复制排序规则 #

命令CREATE COLLATION也可以用来从现有排序规则创建一 个新的排序规则。这有助于在应用程序中使用与操作系统无关的排序规则名称、 创建兼容性名称,或者以更易读的名称来使用 ICU 提供的排序规则。例如:

CREATE COLLATION german FROM "de_DE";
CREATE COLLATION french FROM "fr-x-icu";

23.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-树不能对使用非确定性排 序规则的索引使用去重。此外,某些操作(例如某些模式匹配操作)对非确定性 排序规则来说是不可行的。因此,只有在确实需要时才应使用它们。

Tip

若要处理采用不同 Unicode 规范化形式的文本,另一种办法是使用 normalize函数和is normalized 表达式预处理或检查字符串,而不是使用非确定性排序规则。每种做法都有不 同的权衡。

23.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

许多可用选项会在Section 23.2.3.2中说明;更详尽的 细节则可参见Section 23.2.3.5

23.2.3.1. ICU 比较级别 #

ICU 中两个字符串的比较(排序)由一个多级过程决定,其中各种文本特征被 分组为级别。每个级别的处理由排序规则设置控制。级别 越高,对文本特征的区分就越细。

Table 23.1显示了在给定级别判断相等性时,哪 些文本特征差异会被认为是重要的。Unicode 字符U+2063 是不可见分隔符,如表中所示,在低于identic的所有比 较级别都会被忽略。

Table 23.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 标点符号[a] true true false false false false
identic 全部 true false false false false false

[a] 仅在 ka-shifted时;见Table 23.2


在每个级别上,即使关闭了完全规范化,也会执行基本规范化。例如, 'á'既可能由代码点序列 U&'\0061\0301'表示,也可能由单个代码点 U&'\00E1'表示;即使在identic 级别,这两种表示也会被视为相等。若要把代码点表示上的任何差异都视为不同, 请使用将deterministic设为true创 建的排序规则。

23.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

23.2.3.2. ICU 区域设置的排序规则设置 #

Table 23.2显示了可用的排序规则设置,这 些设置可以作为语言标签的一部分,用来定制排序规则。

Table 23.2. ICU 排序规则设置

默认 描述
co emoji, phonebk, standard, ... standard 排序规则类型。有关其他选项和详细信息,请参见Section 23.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 级

如果其值为trueks设为 level1,则会忽略重音,但仍考虑大小写。

kf upper, lower, false false 若设为upper,大写字母排在小写字母之前;若设为 lower,小写字母排在大写字母之前;若设为 false,排序顺序则取决于该区域设置的规则。
kn true, false false 如果设为true,字符串中的数字会被视为单个数值, 而不是一串数字。例如,'id-45'会排在 'id-123'之前。
kk true, false false

启用完全规范化;这可能影响性能。即使其值为 false,也会执行基本规范化。对那些需要完全规范 化的语言来说,其区域设置通常会默认启用这一选项。

在某些情况下,完全规范化很重要,例如多个重音符应用于同一个字符 时。比如,代码点序列U&'\0065\0323\0302'U&'\0065\0302\0323'都表示一个 e,只不过其抑扬符和下点重音的应用顺序不同。启 用完全规范化后,这两个代码点序列会被视为相等;否则则不相等。

kr space, punct, symbol, currency, digit, script-id  

可设为一个或多个有效值,或者任何 BCP 47 script-id,例如latn拉丁)或grek希腊)。 多个值之间用-分隔。

它会重新定义字符类别的排序顺序;列表中较早出现的字符类别,会排在 列表中较晚类别的前面。例如,值 digit-currency-space(作为语言标签 und-u-kr-digit-currency-space的一部分)会让 标点符号排在数字和空格之前。

ks level1, level2, level3, level4, identic level3 用于确定相等性时的敏感度(或强度),其中 level1对差异最不敏感,而 identic对差异最敏感。详见Table 23.1
kv space, punct, symbol, currency punct 在 3 级比较时会被忽略的字符类别。设为更靠后的值时也会包含更靠前的 值;例如symbol也会把punctspace包含在要忽略的字符中。要使其生效,键 ka必须设为shifted,且键 ks必须设为level3或更低。

默认值可能依赖于具体区域设置。上表并非意在穷举所有情况。有关其他选项和 详细信息,请参见Section 23.2.3.5

Note

对于许多排序规则设置,必须在创建排序规则时把 deterministic设为false,这些设 置才能产生预期效果(参见Section 23.2.2.4)。此外,某些设置只有在键 ka设为shifted时才会生效(参见 Table 23.2)。

23.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'); #

结合了以上两个选项。

23.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

23.2.3.5. ICU 的外部参考 #

本节(Section 23.2.3)只是对 ICU 行为和语言标 签的简要概述。有关技术细节、其他选项和新的行为,请参阅以下文档:

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。