Postgres LIKE '...%' 不使用索引
Posted
技术标签:
【中文标题】Postgres LIKE \'...%\' 不使用索引【英文标题】:Postgres LIKE '...%' doesn't use indexPostgres LIKE '...%' 不使用索引 【发布时间】:2020-04-25 08:04:28 【问题描述】:我有一个表,我想在其中按主键的前缀进行搜索。主键具有03.000221.1
、03.000221.2
、03.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 '...%' 不使用索引的主要内容,如果未能解决你的问题,请参考以下文章