Eu tenho essa consulta bastante básica que é muito lenta por motivos que não consigo descobrir:
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[]))
Onde estou realmente confuso é que o ST_Envelope com a subconsulta é muito rápido por si só
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[]))
E assim é a consulta principal se eu plugar o resultado do 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))
O Postgres não deveria calcular o ST_Envelope uma vez e usá-lo para a condição WHERE, efetivamente fazendo o que eu fiz manualmente? Também não entendo porque nenhum índice é usado para fazer o filtro na consulta original.
Tentei colocar a subconsulta em um CTE, mas isso não resolveu o problema.
A razão é que com a geometria constante, o planejador conhece o valor e estima uma linha de resultado, o que torna uma varredura de índice uma boa estratégia.
Com a consulta original, o planejador não sabe o valor, pois ele é determinado apenas em tempo de execução, então ele estima que haverá 7444984 linhas de resultado.
Eu escreveria duas consultas: uma que calcula a geometria e outra que usa o resultado como constante.