ST_Intersects慢的主因是空间索引失效。根本原因在于几何列被函数包裹(如ST_Transform)或坐标系不一致,导致无法触发GIST索引;正确做法是确保两表几何列均为裸列、同坐标系、均已建GIST索引,并配合&&先粗筛再精算。

ST_Intersects 为什么慢?先看执行计划
直接在 ST_Intersects 上做 JOIN,PostGIS 很可能放弃空间索引,退化成全表扫描。关键不是函数本身,而是它是否能触发 GIST 索引——只有当几何列是独立参数、未被函数包裹或运算干扰时才行。
- 错误写法:
ST_Intersects(ST_Transform(a.geom, 4326), b.geom)→ST_Transform打断索引下推 - 正确前提:两个参与 JOIN 的几何列必须都已建好
GIST索引,且坐标系一致(否则先统一再建索引) - 验证方式:用
EXPLAIN ANALYZE看是否出现Index Scan using xxx_gist on xxx,而不是Seq Scan
JOIN 条件里别碰几何列的任何转换
只要对几何列做 ST_Transform、ST_Buffer、ST_Simplify 或字段拼接(如 a.geom || b.geom),索引就失效。优化核心是“让 ST_Intersects(a.geom, b.geom) 左右两边都是裸列”。
- 提前统一坐标系:用
ALTER TABLE ... ADD COLUMN geom_4326 geometry(Geometry,4326); UPDATE ... SET geom_4326 = ST_Transform(geom, 4326); CREATE INDEX ON table USING GIST (geom_4326); - 避免在 JOIN 中实时计算:比如不要写
ST_Intersects(ST_Centroid(a.geom), b.geom),改用预计算列 + 索引 - 小范围过滤优先:如果业务允许,先用
WHERE b.geom && a.geom(边界框相交)粗筛,再套ST_Intersects精筛
用 && 配合 ST_Intersects 做两层过滤
&& 是 PostGIS 的边界框相交操作符,走 GIST 索引极快,但有误报(返回真不一定真相交)。把它和 ST_Intersects 组合,能大幅减少精算次数。
SELECT a.id, b.name FROM places a JOIN regions b ON a.geom && b.geom AND ST_Intersects(a.geom, b.geom);
- 顺序不能颠倒:
&&必须写在ST_Intersects前面,否则优化器可能不生效 - 只对大表一侧加
&&就行:通常对记录数多的表(如点数据)用其几何列去匹配少的表(如行政区划),效果最明显 - 注意:如果
b.geom是 MultiPolygon 且非常复杂,即使过了&&,ST_Intersects仍可能慢——这时考虑用ST_Subdivide预切分
空间索引不是万能的,注意数据分布和统计信息
PostGIS 依赖 ANALYZE 更新几何列的统计信息。如果某区域点密度极高(比如城市中心),而索引统计不准,优化器可能误判并放弃索引。
- 强制更新:运行
ANALYZE table_name;,尤其在大批量导入或裁剪后 - 检查统计粒度:默认对 geometry 列只采样 100 行,高密度场景可加大,
ALTER TABLE t ALTER COLUMN geom SET STATISTICS 1000; - 小表别硬 JOIN:如果
b表只有几十条记录,用LATERAL+ 子查询反而比 JOIN 更稳,避免优化器选错驱动表
真正卡住的往往不是函数调用本身,而是坐标系不一致、索引没生效、或者统计信息过期——这些地方一错,再怎么调 ST_Intersects 参数都没用。

















