unaccent()阻止Postgres中的索引使用

Alf*_*tig 3 sql postgresql indexing unaccent openstreetmap

我想从导入PostgreSQL 9.3.5的OpenStreetMap数据库中检索具有给定名称的方法,操作系统是Win7 64位.为了有点容错,我使用了Postgres的unaccent扩展.

我的查询如下:

SELECT * FROM germany.ways
WHERE lower(tags->'name') like lower(unaccent('unaccent','Weststrasse'))
Run Code Online (Sandbox Code Playgroud)

查询计划:

Seq Scan on ways  (cost=0.00..2958579.31 rows=122 width=465)
  Filter: (lower((tags -> 'name'::text)) ~~ lower(unaccent('unaccent'::regdictionary, 'Weststrasse'::text)))
Run Code Online (Sandbox Code Playgroud)

奇怪的是,此查询使用顺序扫描方式,尽管索引存在于lower(tags->'name'):

CREATE INDEX ways_tags_name ON germany.ways (lower(tags -> 'name'));
Run Code Online (Sandbox Code Playgroud)

一旦我从查询中删除unaccent,Postgres就会使用索引:

SELECT * FROM germany.ways
WHERE lower(tags->'name') like lower('Weststrasse')
Run Code Online (Sandbox Code Playgroud)

查询计划:

Index Scan using ways_tags_name on ways  (cost=0.57..495.43 rows=122 width=465)
  Index Cond: (lower((tags -> 'name'::text)) = 'weststrasse'::text)
  Filter: (lower((tags -> 'name'::text)) ~~ 'weststrasse'::text)
Run Code Online (Sandbox Code Playgroud)

为什么无法防止Postgres使用索引?在我看来,这没有意义,因为在执行实际查询之前,应该已经完全知道unaccent(变音符号删除等)的结果.所以Postgres应该能够使用索引.使用unaccent时如何避免seq扫描?

Erw*_*ter 13

IMMUTABLE变种 unaccent()

澄清当前接受的错误答案中的错误信息:
表达式索引仅允许IMMUTABLE函数(出于显而易见的原因)并且unaccent()仅是STABLE.您在评论中建议解决方案也存在问题.详细解释和妥善的解决办法 :

根据其内容,tags->name添加unaccent()到表达式索引可能很有用,但这与索引未被使用的问题正交:

实际问题/解决方案

LIKE您的查询中的运算符是巧妙的错误(最有可能).您希望将"Weststrasse"解释为搜索模式,您希望按原样匹配(规范化的)字符串.替换为=运算符,您将看到一个(位图)索引扫描与您当前的索引,无论函数的波动性如何unaccent():

SELECT * FROM germany.ways
WHERE lower(tags->'name') = lower(unaccent('unaccent','Weststrasse'))
Run Code Online (Sandbox Code Playgroud)

为什么?

右操作数LIKE是一种模式.Postgres不能使用普通的btree索引进行模式匹配(例外情况适用).LIKE可以使用btree索引上的相等性检查来优化使用普通字符串作为模式(无特殊字符)的A. 但是如果字符串中有特殊字符,则索引已经输出.

如果IMMUTABLE右边有一个函数LIKE,则可以立即对其进行评估,并且仍然可以进行所述优化.关于函数波动率类别的每个文档:

IMMUTABLE...
此类别允许优化器在查询使用常量参数调用函数时预先评估函数.

功能波动性较小(STABLEVOLATILE)时,这是不可能的.这就是为什么你的伪造"解决方案" IMMUTABLE unaccent()似乎有效,但它确实在口红上涂上了口红.

重申:

  • 如果您想使用LIKE和模式,请使用trigram索引.
  • 如果您不想使用LIKE和模式,请使用相等运算符=

  • @AlfKortig:我只能回答你发布的问题。你问*为什么*,我解释了。许多人在这里阅读并希望能学到一些东西。目前接受的答案是不正确且具有误导性的。你是对的,你的新问题应该放在一个新问题中。评论不是地方。在本例中,简化是错误的。请务必提供完整、正确的图片,并在 psql 中添加 `\d tbl` 的输出。您始终可以链接到此链接以获取上下文。您可以在这里发表评论以引起我的注意。 (2认同)