citext 模块提供大小写不敏感的字符串类型 citext。实质上,它在比较值时会在内部调用 lower。除此之外,它的行为与 text 几乎完全相同。
可以考虑使用 非确定性排序规则(参见 Section 23.2.2.4)来替代该模块。它们可用于大小写不敏感比较、重音不敏感比较以及其他组合,并且能更正确地处理更多 Unicode 特殊情况。
该模块被视为“可信”模块,也就是说,拥有当前数据库 CREATE 权限的非超级用户也可以安装它。
在 PostgreSQL 中进行大小写不敏感匹配的标准做法是在比较值时使用 lower 函数,例如
SELECT * FROM tab WHERE lower(col) = LOWER(?);
这种方式效果尚可,但存在若干缺陷:
它使 SQL 语句变得冗长,并且你必须始终记得在列和查询值两侧都使用 lower。
它不会使用索引,除非你使用 lower 创建函数索引。
如果将列声明为 UNIQUE 或 PRIMARY KEY,隐式生成的索引是区分大小写的。因此它对大小写不敏感搜索毫无用处,也无法以大小写不敏感的方式强制唯一性。
citext 数据类型让你可以在 SQL 查询中省去对 lower 的调用,并允许主键大小写不敏感。citext 与 text 一样感知区域设置,这意味着大小写字符的匹配取决于数据库 LC_CTYPE 设置的规则。这种行为与在查询中使用 lower 完全一致。但由于它由数据类型透明地完成,你无需在查询中记住任何特殊操作。
下面是一个简单的使用示例:
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 语句仍会返回一个元组。
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。
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。