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
来正确处理这种情况。
如果您在文档中发现任何不正确的内容、与您在使用特定功能时的体验不符或需要进一步澄清之处,请使用此表格报告文档问题。