PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

F.9. citext — 大小写不敏感的字符串类型 #

F.9.1. 设计缘由
F.9.2. 使用方法
F.9.3. 字符串比较行为
F.9.4. 限制
F.9.5. 作者

citext 模块提供大小写不敏感的字符串类型 citext。实质上,它在比较值时会在内部调用 lower。除此之外,它的行为与 text 几乎完全相同。

Tip

可以考虑使用 非确定性排序规则(参见 Section 23.2.2.4)来替代该模块。它们可用于大小写不敏感比较、重音不敏感比较以及其他组合,并且能更正确地处理更多 Unicode 特殊情况。

该模块被视为“可信”模块,也就是说,拥有当前数据库 CREATE 权限的非超级用户也可以安装它。

F.9.1. 设计缘由 #

在 PostgreSQL 中进行大小写不敏感匹配的标准做法是在比较值时使用 lower 函数,例如

SELECT * FROM tab WHERE lower(col) = LOWER(?);

这种方式效果尚可,但存在若干缺陷:

  • 它使 SQL 语句变得冗长,并且你必须始终记得在列和查询值两侧都使用 lower。

  • 它不会使用索引,除非你使用 lower 创建函数索引。

  • 如果将列声明为 UNIQUE 或 PRIMARY KEY,隐式生成的索引是区分大小写的。因此它对大小写不敏感搜索毫无用处,也无法以大小写不敏感的方式强制唯一性。

citext 数据类型让你可以在 SQL 查询中省去对 lower 的调用,并允许主键大小写不敏感。citext 与 text 一样感知区域设置,这意味着大小写字符的匹配取决于数据库 LC_CTYPE 设置的规则。这种行为与在查询中使用 lower 完全一致。但由于它由数据类型透明地完成,你无需在查询中记住任何特殊操作。

F.9.2. 使用方法 #

下面是一个简单的使用示例:

CREATE TABLE users (
    nick CITEXT PRIMARY KEY,
    pass TEXT   NOT NULL
);

INSERT INTO users VALUES ( 'larry',  sha256(random()::text::bytea) );
INSERT INTO users VALUES ( 'Tom',    sha256(random()::text::bytea) );
INSERT INTO users VALUES ( 'Damian', sha256(random()::text::bytea) );
INSERT INTO users VALUES ( 'NEAL',   sha256(random()::text::bytea) );
INSERT INTO users VALUES ( 'Bjørn',  sha256(random()::text::bytea) );

SELECT * FROM users WHERE nick = 'Larry';

尽管查询的是 Larry,而 nick 列存储的是 larry,这条 SELECT 语句仍会返回一个元组。

F.9.3. 字符串比较行为 #

citext 通过将每个字符串转换为小写(如同调用了 lower),然后正常比较结果来执行比较。因此,例如,如果 lower 对两个字符串产生相同的结果,这两个字符串就被视为相等。

为了尽可能模拟大小写不敏感的排序规则,许多字符串处理操作符与函数都有 citext 专用版本。例如,正则表达式操作符 ~ 与 ~* 在应用于 citext 时表现出相同的行为:它们都执行大小写不敏感匹配。!~ 与 !~* 也一样,LIKE 操作符 ~~ 与 ~~*、!~~ 与 !~~* 同样如此。如果你想执行大小写敏感匹配,可以将操作符的参数转换为 text。

类似地,当参数为 citext 时,以下所有函数都执行大小写不敏感匹配:

  • regexp_match()

  • regexp_matches()

  • regexp_replace()

  • regexp_split_to_array()

  • regexp_split_to_table()

  • replace()

  • split_part()

  • strpos()

  • translate()

对于 regexp 函数,如果你想执行大小写敏感匹配,可以指定 “c” 标志来强制大小写敏感匹配。否则,你必须在使用这些函数之前将其参数转换为 text。

F.9.4. 限制 #

  • citext 的大小写折叠行为取决于数据库的 LC_CTYPE 设置。因此它比较值的方式在数据库创建时就被确定。按 Unicode 标准的定义,它并非真正的大小写不敏感。实际情况是:只要你对自己的排序规则满意,你就应该会对 citext 的比较满意。但如果数据库中存储了多种语言的数据,当排序规则面向另一种语言时,某种语言的用户可能会发现查询结果与预期不符。

  • 从 PostgreSQL 9.1 开始,你可以为 citext 列或数据值附加 COLLATE 说明。目前,citext 操作符在比较大小写折叠后的字符串时会遵循非默认的 COLLATE 说明,但最初的折叠为小写的步骤总是按照数据库的 LC_CTYPE 设置执行(也就是说,如同指定了 COLLATE "default")。未来的版本可能会改变这一点,使两个步骤都遵循输入的 COLLATE 说明。

  • citext 的效率不如 text,因为操作符函数与 B 树比较函数必须复制数据并将其转换为小写进行比较。此外,只有 text 支持 B 树去重。不过,citext 比使用 lower 实现大小写不敏感匹配的效率略高。

  • 如果你需要数据在某些上下文中大小写敏感比较、在另一些上下文中大小写不敏感比较,citext 帮助不大。标准做法是使用 text 类型,并在需要大小写不敏感比较时手动使用 lower 函数;如果大小写不敏感比较只是偶尔需要,这没什么问题。如果大多数时候需要大小写不敏感行为,偶尔需要大小写敏感行为,可以考虑将数据存储为 citext,并在需要大小写敏感比较时显式将该列转换为 text。无论哪种情况,如果希望两种搜索都快,你都需要两个索引。

  • 包含 citext 操作符的模式必须在当前的 search_path 中(通常是 public);否则将调用普通的大小写敏感 text 操作符。

  • 将字符串转为小写再比较的做法无法正确处理某些 Unicode 特殊情况,例如一个大写字母对应两个小写字母的情形。正因如此,Unicode 区分了 大小写映射 与 大小写折叠。要正确处理这些情况,请使用非确定性排序规则替代 citext。

F.9.5. 作者 #

David E. Wheeler

灵感来自 Donald Fraser 编写的原始 citext 模块。