The Box Is Not The Shape

Two versions of the same population query, against PostGIS tables that both have GiST indexes on geom:

-- A
SELECT sum(b.pop) FROM census_blocks b, neighborhoods n
WHERE n.name = 'West Village' AND ST_Intersects(b.geom, n.geom);
-- 26,718

-- B
SELECT sum(b.pop) FROM census_blocks b, neighborhoods n
WHERE n.name = 'West Village' AND b.geom && n.geom;
-- 49,821

An engineer replaces A with B, reasoning that && uses the spatial index while ST_Intersects does not. What is actually true?

A

ST_Intersects is a function, so A cannot use a GiST index and runs as a sequential scan. The row difference comes from the null geometries A skips.

B

&& tests only whether the two bounding boxes overlap, so B counts blocks near the neighborhood but outside it. ST_Intersects applies && internally first, so A hits the index as well.

C

&& is the exact test and ST_Intersects applies a small tolerance buffer, so B is the more accurate of the two numbers.

D

Both use the index. The difference is that && counts blocks that merely touch the boundary, which ST_Intersects excludes.