st_intersects join慢的根本原因是索引未触发:必须同时满足三前提——函数直接写在on中、两表几何列srid一致、至少一侧建gist索引;任一缺失即退化为全表嵌套循环,且必须配合&&边界框预过滤(&&在前)并确保统计信息准确。

ST_Intersects JOIN 为什么慢得像没建索引
不是函数本身慢,是它根本没用上 GIST 索引。PostgreSQL 优化器只在 ST_Intersects(a.geom, b.geom) 两边都是“裸几何列”时才考虑索引扫描;一旦任一侧被 ST_Transform、ST_Centroid、ST_Buffer 包裹,或字段类型隐式转换(比如 geography 混用),索引立即失效,退化成全表嵌套循环——10 万 × 10 万 = 100 亿次计算不是夸张。
- 用
EXPLAIN ANALYZE看执行计划:出现Seq Scan或Nested Loop但没Index Scan using xxx_gist,基本就是索引没触发 - 检查
pg_stat_all_indexes中对应索引的idx_scan是否为 0 - 确认两表几何列 SRID 一致:
SELECT ST_SRID(geom) FROM table LIMIT 1,不一致就别谈性能
必须满足的三个硬性前提
缺一不可。少一个,ST_Intersects 就只是个纯 CPU 计算函数,和写 WHERE a.x > b.x 没区别。
-
ST_Intersects必须直接写在JOIN ... ON条件里,不能挪到WHERE子句(否则先笛卡尔积再过滤) - 两表几何列必须同 SRID:用
ST_Transform统一后重建索引,别依赖隐式转换 - 至少一侧表的几何列已建 GIST 索引:
CREATE INDEX ON table USING GIST (geom);若双侧都有,优化器通常选小表作驱动表
&& 边界框预过滤不是可选项,是必选项
&& 是唯一能高效走 GIST 索引的空间操作符,它只比对 MBR(最小边界矩形),快一个数量级。但它有误报——返回 TRUE 不代表真相交,所以必须和 ST_Intersects 配合使用,且顺序不能颠倒。
- 正确写法:
ON a.geom && b.geom AND ST_Intersects(a.geom, b.geom);&&必须在前,否则优化器可能忽略索引剪枝 - 优先在记录数多的表上加
&&过滤:比如 500 万点数据 JOIN 500 个行政区,应在点表侧加WHERE a.geom && ST_MakeEnvelope(...) - 如果面数据极复杂(如全国省界含百万节点),考虑预切分:
ST_Subdivide(b.geom, 256)再 JOIN,避免单次ST_Intersects卡死
容易被忽略的统计信息和类型陷阱
即使语法、索引、SRID 全对,查询仍慢?大概率是优化器“猜错了”。PostGIS 严重依赖 ANALYZE 更新的统计信息来估算选择性,而几何列的分布极不均匀(城市中心点密度可能是郊区的千倍)。
- 执行
ANALYZE table_name;强制刷新统计信息,特别是刚批量导入或坐标系转换后 - 别用
geography列跑ST_Intersects:它对空间索引支持有限,优先转为geometry并指定 SRID - WKT 字符串或经纬度字段必须显式转几何:
ST_SetSRID(ST_MakePoint(lon, lat), 4326),不能直接传lon, lat给函数
ST_Intersects,而在怎么让 PostgreSQL 相信“走索引确实更快”——而这取决于你有没有给它准确的元数据、干净的几何列、以及足够确定的过滤边界。










