我想从导入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扫描?
答案 0 :(得分:12)
unaccent()
澄清currently accepted, incorrect answer中的错误信息:
表达式索引仅允许IMMUTABLE
个函数(出于显而易见的原因),而unaccent()
仅为STABLE
。 solution 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时预先评估函数 查询使用常量参数调用它。
使用较小的函数波动率(STABLE
或VOLATILE
)时,这是不可能的。这就是为什么你伪造IMMUTABLE unaccent()
的“解决方案”似乎有效,但它确实在口红上涂上了口红。
重申:
LIKE
和模式,请使用trigram index。LIKE
和模式,请使用等于运算符=