为什么 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)
不过,我仍然很好奇为什么第一个查询的行为与此不同。
我正在使用 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)
不过,我仍然很好奇为什么第一个查询的行为与此不同。