由于我无法弄清楚的原因,我有这个相当基本的查询非常慢:
SELECT s.id
FROM segments s
WHERE
ST_DWithin(
s.geom::GEOGRAPHY,
ST_Envelope((SELECT ST_COLLECT(s2.geom) FROM segments s2 WHERE s2.id IN (407820025, 407820024, 407817407, 407817408, 407816908, 407816909, 407817413, 407817414, 407817409, 407817410, 407817405, 407817406, 407816905, 407816907, 407817412, 407817411, 407816906, 407816904, 407816764, 407816765)))::GEOGRAPHY,
30
);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Seq Scan on segments s (cost=55.58..48476381.06 rows=7444984 width=4)
Filter: st_dwithin((geom)::geography, (st_astext(st_envelope($0)))::geography, '30'::double precision)
InitPlan 1 (returns $0)
-> Aggregate (cost=55.57..55.58 rows=1 width=32)
-> Index Scan using segments_pkey on segments s2 (cost=0.44..55.52 rows=20 width=113)
Index Cond: (id = ANY ('{407820025,407820024,407817407,407817408,407816908,407816909,407817413,407817414,407817409,407817410,407817405,407817406,407816905,407816907,407817412,407817411,407816906,407816904,407816764,407816765}'::integer[]))
我真正感到困惑的是带有子查询的 ST_Envelope 本身非常快
SELECT ST_Envelope((SELECT ST_COLLECT(geom) FROM segments WHERE id IN (407820025, 407820024, 407817407, 407817408, 407816908, 407816909, 407817413, 407817414, 407817409, 407817410, 407817405, 407817406, 407816905, 407816907, 407817412, 407817411, 407816906, 407816904, 407816764, 407816765)))::GEOGRAPHY;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Result (cost=55.58..55.60 rows=1 width=32)
InitPlan 1 (returns $0)
-> Aggregate (cost=55.57..55.58 rows=1 width=32)
-> Index Scan using segments_pkey on segments (cost=0.44..55.52 rows=20 width=113)
Index Cond: (id = ANY ('{407820025,407820024,407817407,407817408,407816908,407816909,407817413,407817414,407817409,407817410,407817405,407817406,407816905,407816907,407817412,407817411,407816906,407816904,407816764,407816765}'::integer[]))
如果我插入 ST_Envelope 的结果,主查询也是如此
SELECT id
FROM segments
WHERE
st_dwithin(
geom::geography,
'0103000020E61000000100000005000000C87B6E0D8FB85EC04BFD8462B9C34640C87B6E0D8FB85EC0929B35C16DC44640BBF8DDA6F2B75EC0929B35C16DC44640BBF8DDA6F2B75EC04BFD8462B9C34640C87B6E0D8FB85EC04BFD8462B9C34640'::GEOGRAPHY,
30
);
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Index Scan using segments_geom_geo_idx on segments (cost=0.42..4.82 rows=1 width=4)
Index Cond: ((geom)::geography && '0103000020E61000000100000005000000C87B6E0D8FB85EC04BFD8462B9C34640C87B6E0D8FB85EC0929B35C16DC44640BBF8DDA6F2B75EC0929B35C16DC44640BBF8DDA6F2B75EC04BFD8462B9C34640C87B6E0D8FB85EC04BFD8462B9C34640'::geography)
Filter: (('0103000020E61000000100000005000000C87B6E0D8FB85EC04BFD8462B9C34640C87B6E0D8FB85EC0929B35C16DC44640BBF8DDA6F2B75EC0929B35C16DC44640BBF8DDA6F2B75EC04BFD8462B9C34640C87B6E0D8FB85EC04BFD8462B9C34640'::geography && _st_expand((geom)::geography, '30'::double precision)) AND _st_dwithin((geom)::geography, '0103000020E61000000100000005000000C87B6E0D8FB85EC04BFD8462B9C34640C87B6E0D8FB85EC0929B35C16DC44640BBF8DDA6F2B75EC0929B35C16DC44640BBF8DDA6F2B75EC04BFD8462B9C34640C87B6E0D8FB85EC04BFD8462B9C34640'::geography, '30'::double precision, true))
Postgres 不应该计算一次 ST_Envelope 然后将其用于 WHERE 条件,有效地执行我手动执行的操作吗?我也不明白为什么在原始查询中没有使用索引来执行过滤器。
我尝试将子查询放在 CTE 中,但这并没有解决问题。
原因是在几何形状不变的情况下,规划器知道值并估计一个结果行,这使得索引扫描成为一个很好的策略。
使用原始查询,planner 不知道该值,因为它只是在执行时确定,所以它猜测会有 7444984 个结果行。
我会写两个查询:一个计算几何,一个使用结果作为常数。