选择 打开 改范围 完整检索页
受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10
当前 PostgreSQL 版本不在支持生命周期内。
您可以参阅当前版本的对应页面,或其他在上面列出的活跃大版本。

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. 标准排序规则

所有平台都提供名为 defaultCPOSIX 的排序规则。根据操作系统支持情况,还可能提供其他排序规则。default 排序规则选择创建数据库时指定的 LC_COLLATELC_CTYPE 值。CPOSIX 排序规则都采用传统 C行为,仅将 ASCII 字母 AZ 视为字母,并严格按字符编码的字节值排序。

此外,编码 UTF8 还可以使用 SQL 标准排序规则名 ucs_basic。它等价于 C,按 Unicode 码点排序。

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 排序规则

除了 initdb 预装载的基本语言与国家组合外,ICU 还允许进一步定制排序规则。建议用户定义自己的排序规则对象,利用这些功能使排序行为满足自己的需求。关于 ICU 区域设置名称的信息,参见 https://unicode-org.github.io/icu/userguide/locale/https://unicode-org.github.io/icu/userguide/collation/api.html。可接受的名称和属性集合取决于具体的 ICU 版本。

以下是一些示例:

CREATE COLLATION "de-u-co-phonebk-x-icu" (provider = icu, locale = 'de-u-co-phonebk');
CREATE COLLATION "de-u-co-phonebk-x-icu" (provider = icu, locale = 'de@collation=phonebook');

采用电话簿排序类型的德语排序规则

第一个示例使用 BCP 47 定义的语言标签来选择 ICU 区域设置。第二个示例使用传统的 ICU 专有区域设置语法。今后应优先采用第一种形式,但较旧的 ICU 版本不支持它。

注意,可以在 SQL 环境中为排序规则对象任意命名。本例遵循预定义排序规则所采用的命名风格,该风格也遵循 BCP 47,但用户定义的排序规则并不要求如此。

CREATE COLLATION "und-u-co-emoji-x-icu" (provider = icu, locale = 'und-u-co-emoji');
CREATE COLLATION "und-u-co-emoji-x-icu" (provider = icu, locale = '@collation=emoji');

采用 Unicode 技术标准 #51 所定义 Emoji 排序类型的根排序规则

注意,传统的 ICU 区域设置命名系统使用空字符串来选择根区域设置。

CREATE COLLATION latinlast (provider = icu, locale = 'en-u-kr-grek-latn');
CREATE COLLATION latinlast (provider = icu, locale = 'en@colReorder=grek-latn');

将希腊字母排在拉丁字母之前。(默认是拉丁字母在希腊字母之前。)

CREATE COLLATION upperfirst (provider = icu, locale = 'en-u-kf-upper');
CREATE COLLATION upperfirst (provider = icu, locale = 'en@colCaseFirst=upper');

将大写字母排在小写字母之前。(默认是小写字母在前。)

CREATE COLLATION special (provider = icu, locale = 'en-u-kf-upper-kr-grek-latn');
CREATE COLLATION special (provider = icu, locale = 'en@colCaseFirst=upper;colReorder=grek-latn');

组合上述两个选项。

CREATE COLLATION numeric (provider = icu, locale = 'en-u-kn-true');
CREATE COLLATION numeric (provider = icu, locale = 'en@colNumeric=yes');

数值排序,按数值对数字序列排序,例如:A-21 < A-123(也称自然排序)。

参见Unicode 技术标准 #35BCP 47了解详情。可用排序类型列表(co子标签)可在以下位置找到:CLDR 仓库

注意,虽然此系统允许创建忽略大小写忽略重音等排序规则(使用 ks 键),但要让这些排序规则真正不区分大小写或重音,还必须在 CREATE COLLATION 中将它们声明为非确定性排序规则;参见 Section 23.2.2.4。否则,按排序规则比较相等、但字节不相等的字符串,仍会按字节值排序。

Note

ICU 的设计允许接受几乎任何字符串作为区域设置名,并按照其文档描述的回退流程,匹配到它能够提供的最接近区域设置。因此,如果排序规则定义使用了当前 ICU 安装实际不支持的功能,不会得到直接反馈。所以建议创建应用层面的测试用例,以检查排序规则定义是否满足需求。

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 规范形式的字符串之间的比较。这些不敏感的比较由排序规则提供者实际实现;确定性标志只决定是否使用逐字节比较来打破平局。关于术语的更多信息,另见 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 表达式预处理或检查字符串,而不是使用非确定性排序规则。每种做法都有不 同的权衡。