未使用数据类型 citext 的列上的索引

Jos*_*uis 3 postgresql index pattern-matching case-sensitive

在 PostgreSQL 9.4 中,具有以下架构:

CREATE TABLE people (
    id INTEGER PRIMARY KEY,
    name TEXT,
    junk CHAR(1000)
);

INSERT INTO people(id, name)
 SELECT generate_series(1,100000), md5(random()::text);

CREATE INDEX ON people (name text_pattern_ops);
Run Code Online (Sandbox Code Playgroud)

如果我按名称搜索,则使用索引:

test=# explain analyze select id, name from people where name like 'a%';
                                                           QUERY PLAN                                                           
--------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on people  (cost=248.59..1160.92 rows=6061 width=37) (actual time=2.412..8.340 rows=6271 loops=1)
   Filter: (name ~~ 'a%'::text)
   Heap Blocks: exact=834
   ->  Bitmap Index Scan on people_name_idx  (cost=0.00..247.08 rows=6266 width=0) (actual time=2.123..2.123 rows=6271 loops=1)
         Index Cond: ((name ~>=~ 'a'::text) AND (name ~<~ 'b'::text))
 Planning time: 0.600 ms
 Execution time: 8.991 ms
Run Code Online (Sandbox Code Playgroud)

但如果我替换TEXTCITEXT

CREATE EXTENSION CIText;

CREATE TABLE people (
    id INTEGER PRIMARY KEY,
    name CITEXT,
    junk CHAR(1000)
);
Run Code Online (Sandbox Code Playgroud)

不再使用索引:

test=# explain analyze select id, name from people where name like 'a%';
                                                QUERY PLAN                                                 
-----------------------------------------------------------------------------------------------------------
 Seq Scan on people  (cost=0.00..2084.00 rows=500 width=36) (actual time=5.700..152.572 rows=6305 loops=1)
   Filter: (name ~~ 'a%'::citext)
   Rows Removed by Filter: 93695
 Planning time: 0.764 ms
 Execution time: 153.046 ms
Run Code Online (Sandbox Code Playgroud)

根据CITEXTPostgreSQL 文档,行为应该与TEXT

否则,它的行为几乎与文本完全一样。

如何告诉 PostgreSQL 使用索引?

Erw*_*ter 5

索引使用text_pattern_ops(以及使用C语言环境时的默认运算符类)取决于字符数据的二进制表示citext在保留大小写的情况下存储原始值,所以一定有问题......

就像您评论的那样,实际原因隐藏在整理支持中

无论哪种方式,citext或者text,您都可以使其与表达式索引一起使用:

CREATE INDEX people_name_idx ON people (lower(name) text_pattern_ops);
Run Code Online (Sandbox Code Playgroud)

以及相应的查询:

SELECT id, name FROM people WHERE lower(name) LIKE 'abc%';
Run Code Online (Sandbox Code Playgroud)

请注意,即使在提供lower(name)数据时text,也会返回数据类型citext

或者,您可以使用 trigram 索引,它的维护成本更高,但也提供更多功能:

旁白:你的测试用例是次优的,因为你的虚拟值是开始与所有小写的模式'a%'往往是不足够的选择性使用索引在所有。并且char(1000)没有意义(即使与测试无关)。