为什么 PostgreSQL 在此查询中执行顺序扫描而不是索引扫描?

Why is PostgreSQL doing a sequential scan instead of a index scan in this query?

我正在使用 OpenStreetMap osm2pgsql 数据库。其中一个 table (planet_osm_line) 有两个索引字段:osm_id (int, primary key) 和 way (postgis geometry).

我想找出哪些街道与特定街道相交,我知道它是 osm_id。所以我这样做:

SELECT name, * FROM planet_osm_line
WHERE highway IS NOT NULL
AND osm_id != 126021312
AND ST_Intersects(way, (SELECT way FROM planet_osm_line WHERE osm_id = 126021312 LIMIT 1))

并且 运行 大约需要 10 秒。

如果相反,我将该子查询取出并 运行 分开,它看起来像这样:

SELECT name, * FROM planet_osm_line
WHERE highway IS NOT NULL
AND osm_id != 126021312
AND ST_Intersects(way, '010200002031BF0D000D000000E17...')

并且 运行 大约需要 0.47 秒。

运行 EXPLAIN 在第一个和第二个查询中给出了有关差异的提示。

第一个:

Seq Scan on planet_osm_line  (cost=2.09..614596.67 rows=628706 width=1079)
  Filter: ((highway IS NOT NULL) AND (osm_id <> 126021312) AND st_intersects(way, [=12=]))
  InitPlan 1 (returns [=12=])
    ->  Limit  (cost=0.43..2.09 rows=1 width=249)
          ->  Index Scan using planet_osm_line_pkey on planet_osm_line planet_osm_line_1  (cost=0.43..3.76 rows=2 width=249)
                Index Cond: (osm_id = 126021312)

第二个:

Index Scan using planet_osm_line_index on planet_osm_line  (cost=0.41..4.25 rows=1 width=1079)
  Index Cond: (way && '010200002031BF0D000D000000E17...'::geometry)
  Filter: ((highway IS NOT NULL) AND (osm_id <> 126021312) AND _st_intersects(way, '010200002031BF0D000D000000E17...'::geometry))

为什么 PostgreSQL 第一次做序列扫描,第二次做索引扫描?有没有办法不用发出两个查询就可以解决这个问题?

重写你的查询,这样你就不会在 ST_Intersects 中有一个子查询,而是在 FROM 中有一个交叉连接,然后它将被 WHERE 中的交叉点限制(这也隐含地做&&,即边界框检查,它将命中空间索引)。

SELECT name, osm.* 
FROM planet_osm_line osm, 
  (SELECT way FROM planet_osm_line WHERE osm_id = 126021312 LIMIT 1) line
WHERE highway IS NOT NULL
AND osm_id != 126021312
AND ST_Intersects(osm.way, line.way);

这种方式似乎工作正常(部分回答了我的问题):

SELECT l1.name, l1.*
FROM planet_osm_line AS l1
INNER JOIN planet_osm_line AS l2
ON ST_Intersects(l1.way, l2.way)
WHERE l1.highway IS NOT NULL
AND l1.osm_id != 126021312
AND l2.osm_id = 126021312

它的解释表明 PostgreSQL 似乎首先在做我想做的事情:

Nested Loop  (cost=6.80..577.98 rows=7451 width=1108)
  ->  Index Scan using planet_osm_line_pkey on planet_osm_line l2  (cost=0.43..3.76 rows=2 width=249)
      Index Cond: (osm_id = 126021312)
  ->  Bitmap Heap Scan on planet_osm_line l1  (cost=6.37..286.48 rows=63 width=1108)
        Recheck Cond: (way && l2.way)
        Filter: ((highway IS NOT NULL) AND (osm_id <> 126021312) AND _st_intersects(way, l2.way))
        ->  Bitmap Index Scan on planet_osm_line_index  (cost=0.00..6.36 rows=206 width=0)
            Index Cond: (way && l2.way)

不过,我仍然很好奇为什么第一个查询的行为与此不同。