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

时间:2015-03-06 12:42:44

标签: sql postgresql indexing openstreetmap unaccent

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

我的查询如下:

SELECT * FROM germany.ways
WHERE lower(tags->'name') like lower(unaccent('unaccent','Weststrasse'))

查询计划:

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

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

CREATE INDEX ways_tags_name ON germany.ways (lower(tags -> 'name'));

Postgres在我从查询中移除unaccent后立即使用索引:

SELECT * FROM germany.ways
WHERE lower(tags->'name') like lower('Weststrasse')

查询计划:

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)

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

1 个答案:

答案 0 :(得分:12)

unaccent()

的IMMUTABLE变体

澄清currently accepted, incorrect answer中的错误信息:
表达式索引仅允许IMMUTABLE个函数(出于显而易见的原因),而unaccent()仅为STABLEsolution you suggested in the the comment也存在问题。 的详细解释和正确的解决方案

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

实际问题/解决方案

您的查询中的运营商LIKE巧妙地错误(最有可能)。您想要将'Weststrasse'解释为搜索模式,您希望按原样匹配(规范化的)字符串。替换为 = 运算符,您将看到一个(位图)索引扫描,其中包含unaccent()函数易变性的当前索引 irregardless

SELECT * FROM germany.ways
WHERE lower(tags->'name') = lower(unaccent('unaccent','Weststrasse'))

为什么?

LIKE的右操作数是模式。 Postgres不能使用普通的btree索引进行模式匹配(exceptions apply)。可以使用btree索引上的相等性检查来优化具有普通字符串作为模式的LIKE(无特殊字符)。但是如果字符串中有特殊字符,索引就会出现。

如果IMMUTABLE右侧有LIKE函数,则可以立即对其进行评估,并且仍然可以进行所述优化。每documentation on Function Volatility Categories

  

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

使用较小的函数波动率(STABLEVOLATILE)时,这是不可能的。这就是为什么你伪造IMMUTABLE unaccent()的“解决方案”似乎有效,但它确实在口红上涂上了口红。

重申:

  • 如果您想使用LIKE和模式,请使用trigram index
  • 如果您不想使用LIKE和模式,请使用等于运算符=