受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

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

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

Tip

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

这个模块被认为是受信任的,也就是说,它可以由在当前数据库上具有 CREATE权限的非超级用户安装。

F.7.1. 原理 #

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

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

这种做法效果尚可,但有几个缺点:

  • 它会让 SQL 语句变得冗长,而且你必须时刻记得同时对列和查询值调用 lower

  • 除非你创建一个使用lower的函数索引,否则它不会使用索引。

  • 如果把列声明为UNIQUEPRIMARY KEY,隐式生成的索引仍然是大小写敏感的。因此,它既无法用于大小写不敏感搜索,也不能以大小写不敏感的方式强制唯一性。

citext数据类型允许你在 SQL 查询中省去对 lower的调用,并允许主键不区分大小写。 与text一样,citext也与区域设置相关,这意味着大写字符和小写字符如何匹配取决于数据库 LC_CTYPE设置的规则。同样,这种行为与在查询中使用 lower完全一致。但由于这是由数据类型透明地完成的, 因此你不必在查询中额外记住任何特殊处理。

F.7.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';

即使nick列中存的是larry, 而查询条件写的是Larry,这个SELECT语句仍会返回一行。

F.7.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()

对于正则表达式函数,如果你想按大小写敏感方式匹配,可以指定 c标志来强制大小写敏感匹配。否则,若要获得大小写敏感行为, 就必须在调用这些函数之前先把值转换为text

F.7.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.7.5. 作者 #

David E. Wheeler

灵感来自 Donald Fraser 最初编写的citext模块。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。