citext 模块提供了一种默认不区分大小写的字符类型 citext。本质上,它在比较值时内部调用 lower。除此之外,它的行为与 text 类型几乎完全相同。
考虑使用“非确定性排序规则”(请参阅 第 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';
SELECT 语句将返回一个元组,尽管 nick 列设置为 larry 并且查询是针对 Larry 的。
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()
对于正则表达式函数,如果您想进行区分大小写的匹配,可以指定“c”标志来强制进行区分大小写的匹配。否则,如果您想要区分大小写的行为,则必须在使用这些函数之前将数据转换为 text。
citext 的大小写转换行为取决于数据库的 LC_CTYPE 设置。因此,它的值比较方式在数据库创建时就已确定。它并不是严格按照 Unicode 标准定义的“大小写不敏感”。实际上,这意味着,只要您对您的排序规则满意,您就会对 citext 的比较满意。但如果您数据库中存储了不同语言的数据,一种语言的用户可能会发现他们的查询结果不符合预期,因为排序规则是为另一种语言设置的。
从 PostgreSQL 9.1 开始,您可以为 citext 列或数据值附加 COLLATE 规范。目前,citext 运算符在比较大小写转换后的字符串时会遵循非默认的 COLLATE 规范,但最初的大小写转换始终根据数据库的 LC_CTYPE 设置(即,如同给出了 COLLATE "default")进行。这可能在未来的版本中进行更改,以便两个步骤都遵循输入的 COLLATE 规范。
citext 的效率不如 text,因为运算符函数和 B-tree 比较函数必须复制数据并将其转换为小写进行比较。此外,只有 text 支持 B-Tree 重复数据删除。但是,citext 比使用 lower 进行不区分大小写的匹配效率稍高。
如果您需要在某些上下文中区分大小写,而在其他上下文中不区分大小写地比较数据,那么 citext 帮助不大。标准答案是使用 text 类型,并在需要不区分大小写比较时手动调用 lower 函数;如果仅偶尔需要不区分大小写比较,这效果还不错。如果您大部分时间需要不区分大小写行为,而偶尔需要区分大小写,请考虑将数据存储为 citext,并在需要区分大小写比较时显式将该列转换为 text。在这两种情况下,如果您希望两种搜索都快速,您都需要两个索引。
包含 citext 运算符的模式必须位于当前 search_path 中(通常是 public);如果不在,则会调用正常的区分大小写的 text 运算符。
为了比较而将字符串转换为小写的方法不能正确处理某些 Unicode 特殊情况,例如当一个大写字母有两个小写字母对应的情况。Unicode 因此区分“大小写映射”和“大小写折叠”。请使用非确定性排序规则而不是 citext 来正确处理这种情况。