st_distance返回0或报错是因为默认按平面坐标系计算,单位为“度”且无地理意义;若要获得以米为单位的球面距离,必须确保两个参数均为geography类型,例如st_distance(point1::geography, point2::geography)。

ST_Distance 为什么返回 0 或报错?
PostGIS 的 ST_Distance 默认在平面坐标系(Cartesian)下计算欧氏距离,若直接传入经纬度(WGS84,SRID=4326),结果单位是“度”,数值极小且无地理意义;更常见的是传入几何类型(GEOMETRY)但未指定 SRID,或两个点坐标类型不一致(一个 POINT 是 GEOMETRY,另一个是 GEOGRAPHY),导致函数无法匹配签名而报错 function st_distance(geometry, geometry) does not exist。
关键判断:你要算的是「球面大圆距离(米)」,就必须用 GEOGRAPHY 类型,而非默认的 GEOMETRY。
- 确保两点都显式转为
GEOGRAPHY:ST_Distance(point1::geography, point2::geography) - 如果原始字段是
GEOMETRY(如geom列),先用ST_Transform(geom, 4326)统一到 WGS84,再转::geography - 避免混用类型:不要写
ST_Distance(geom1, geom2::geography)—— 类型不匹配会失败
怎么写才真正返回「米」?
ST_Distance 只有在两个参数都是 GEOGRAPHY 时,才按 WGS84 椭球模型计算真实地表距离,单位为米。这是唯一能直接得到可读距离的方式。
示例(计算北京和上海两坐标直线距离):
SELECT ST_Distance( ST_MakePoint(116.4074, 39.9042)::geography, -- 北京 ST_MakePoint(121.4737, 31.2304)::geography -- 上海 );
返回约 1213776.5(米,即 ~1214 km)。
- 注意经纬度顺序:
ST_MakePoint(longitude, latitude),反了会导致位置偏差极大 - 如果数据存的是字符串或分离的
lon/lat字段,必须先拼成POINT再转geography,不能直接对数值列调用ST_Distance - 性能提示:在
GEOGRAPHY上使用ST_Distance无法利用GEOMETRY的空间索引,如需高频查询,考虑预计算或用ST_DWithin配合索引过滤
替代方案:ST_DistanceSphere vs ST_Distance
如果你用的是较老版本 PostGIS(ST_DistanceSphere 是备选:它强制用球面模型(非椭球),输入可为 GEOMETRY,但需指定 SRID=4326,例如:
SELECT ST_DistanceSphere( ST_SetSRID(ST_MakePoint(116.4074, 39.9042), 4326), ST_SetSRID(ST_MakePoint(121.4737, 31.2304), 4326) );
它比 ST_Distance(...::geography) 稍快,但精度略低(误差通常在 0.3% 以内)。
-
ST_Distance(geography)→ 椭球模型,高精度,推荐用于生产 -
ST_DistanceSphere→ 球面模型,兼容旧版,适合对精度要求不苛刻的场景 - 两者都不接受
text或numeric直接输入;必须构造为点对象并带正确空间参考
常见错误:WHERE 条件里用错类型导致全表扫描
写 WHERE ST_Distance(a.geom, b.geom) 看似合理,但因 <code>a.geom 是 GEOMETRY,实际算的是平面度数距离,1000 度 ≈ 整个地球周长,逻辑完全错乱;更糟的是,该表达式无法走空间索引,必然触发全表扫描。
正确做法分两步:
- 先用
ST_DWithin(支持索引)粗筛:WHERE ST_DWithin(a.geom::geography, b.geom::geography, 1000) - 再用
ST_Distance精排或过滤(只作用于已缩小的结果集) - 确保
geom列上有GIST索引;若常查地理距离,建议额外建geography表达式索引:CREATE INDEX idx_geom_geog ON tbl USING GIST ((geom::geography))
最易被忽略的一点:哪怕你只查一次两点距离,也必须确认输入是否真为 GEOGRAPHY —— 少一个 ::geography,结果就不是米,而是毫无业务意义的数字。










