Postgres LIKE '...%' 不使用索引

Posted

技术标签:

【中文标题】Postgres LIKE \'...%\' 不使用索引【英文标题】:Postgres LIKE '...%' doesn't use indexPostgres LIKE '...%' 不使用索引 【发布时间】:2020-04-25 08:04:28 【问题描述】:

我有一个表,我想在其中按主键的前缀进行搜索。主键具有03.000221.103.000221.203.000221.3 等值,我想检索所有以03.000221. 开头的值。

我的第一个想法是使用 LIKE '03.000221.%' 进行过滤,认为 Postgres 足够聪明,可以在索引中查找 03.000221. 并从该点执行范围扫描。但是不,这会执行顺序扫描。

                                                   QUERY PLAN                                                    
-----------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..253626.34 rows=78 width=669)
   Workers Planned: 2
   ->  Parallel Seq Scan on ...  (cost=0.00..252618.54 rows=32 width=669)
         Filter: ((id ~~ '03.000221.%'::text)
 JIT:
   Functions: 2
   Options: Inlining false, Optimization false, Expressions true, Deforming true

如果我使用普通的 >=< 范围进行等效操作,例如。 G。 id >= '03.000221.' and id < '03.000221.Z' 它确实使用了索引:

                                                                 QUERY PLAN                                                                  
---------------------------------------------------------------------------------------------------------------------------------------------
 Index Scan using ... on ...  (cost=0.56..8.58 rows=1 width=669)
   Index Cond: ((id >= '03.000221.'::text) AND (id < '03.000221.Z'::text))

但这更脏,在我看来,Postgres 应该能够推断出它可以使用LIKE 进行等效的索引范围查找。为什么不呢?

【问题讨论】:

【参考方案1】:

如果您使用 text_pattern_ops 运算符构建索引,或者您使用 C 排序规则,PostgreSQL 将执行此操作。

如果您使用一些随机的其他排序规则,PostgreSQL 就无法推断出太多关于它的任何东西。在非常常见的“en_US.utf8”排序规则中观察这一点。

select * from (values ('03.000221.1'), ('03.0002212'), ('03.000221.3')) f(x) order by x;
      x      
-------------
 03.000221.1
 03.0002212
 03.000221.3

这自然会导致您的查询出现错误答案:

select * from (values ('03.000221.1'), ('03.0002212'), ('03.000221.3')) f(id)
    where ((id >= '03.000221.'::text) AND (id < '03.000221.Z'::text))
     id      
-------------
 03.000221.1
 03.0002212
 03.000221.3

【讨论】:

对于给定的值,collate "C" 可能是最好的选择 我使用的是 C.UTF-8,这显然还不够 C。谢谢! @ToniCárdenas 我从来不理解 C 和 C.UTF-8 之间的区别。我认为也许 C 是作为一种特殊情况在内部实现的,而 C.UTF-8 是外包给 glibc 的。它可能可以通过 C.UTF-8 使用索引并获得正确答案,只是它不知道它可以。

以上是关于Postgres LIKE '...%' 不使用索引的主要内容,如果未能解决你的问题,请参考以下文章

使用LIKE的Postgres节点搜索查询,如何设置%

匹配h2 / postgres中的文字百分比?

Slick Postgres:如何使用 like 运算符在字符串列表中搜索

匹配 h2/postgres 中的文字百分比?

postgres中大型数据库的索引

Postgres:强制分析器使用位图扫描而不是索引扫描