Postgres 更喜欢昂贵的 ST_Intersects() 而不是便宜的索引

Postgres preferring costly ST_Intersects() over cheap index

我正在使用 Postgres 9.4 对 OSM 的完整行星转储执行一个相当简单的查询。我想要做的是获取属于德国 A8 高速公路的所有方式。在准备步骤中,我为所有行政边界关系创建了多边形并将它们存储在 table 多边形中,这样我就可以进行更简单的空间相交测试。为了实现快速查询处理,我还为 'ref' hstore 标签创建了一个索引:

CREATE INDEX idx_ways_tags_ref ON planet_20141222.ways USING btree (lower(tags->'ref'));

另外,我已经通过之前的查询得到了德国行政边界的id(结果id = 51477)

我的数据库模式是正常的 API 0.6 模式,数据是通过转储方法导入到 Postgres 中的(使用 pgsnapshot_schema_0.6*.sql 随附的脚本渗透)。还对所有 table 执行了 VACUUM ANALYZE。

有问题的查询如下所示:

SELECT DISTINCT wy.id FROM planet_20141222.ways wy, planet_20141222.polygons py
WHERE py.id=51477 AND ST_Intersects(py.geom,wy.linestring) AND ((wy.tags->'highway') is not null) AND (lower(wy.tags->'ref') like lower('A8'));

这个查询的运行时间很糟糕,因为 Postgres 更喜欢昂贵的 ST_Intersects() 测试而不是 'ref' 上的廉价(和高选择性)索引。去除交集测试时,查询returns在几毫秒内。

我该怎么做才能让 Postgres 首先评估查询中存在索引的部分,而不是在整个星球上测试与德国的交集的每条路?

我目前的解决方案是将 SQL 查询拆分为两个单独的查询。第一个进行索引支持的标签测试,第二个进行空间相交测试。我想 Postgres 可以做得更好,但是怎么做呢?

编辑:

a) OSM 0.6 导入脚本在 table:

的方式上创建以下索引
CREATE INDEX idx_ways_bbox ON ways USING gist (bbox);
CREATE INDEX idx_ways_linestring ON ways USING gist (linestring);

b) 此外,我在多边形上创建了另一个索引:

CREATE INDEX polygons_geom_tags on polygons using gist(geom, tags);

c) 查询 without ST_Intersects() 的 EXPLAIN ANALYZE 输出如下所示:

"Index Scan using ways_tags_ref on ways  (cost=0.57..4767.61 rows=1268 width=467) (actual time=0.064..0.267 rows=60 loops=1)"
"  Index Cond: (lower((tags -> 'ref'::text)) = 'a8'::text)"
"  Filter: (((tags -> 'highway'::text) IS NOT NULL) AND (lower((tags -> 'ref'::text)) ~~ 'a8'::text))"
"  Rows Removed by Filter: 5"
"Total runtime: 0.300 ms"

使用ST_Intersects()的查询运行时间超过15分钟,所以我取消了。

也许试试这样的东西..?

WITH wy AS (
  SELECT * FROM planet_20141222.ways 
  WHERE ((tags->'highway') IS NOT null)
  AND (lower(tags->'ref') LIKE lower('A8'))
)
SELECT DISTINCT wy.id 
FROM wy, planet_20141222.polygons py
WHERE py.id=51477 
  AND ST_Intersects(py.geom,wy.linestring);