MySQL 8.0.17+ 才真正支持球面距离计算,需字段为 SRID 4326 的 POINT 类型,并用 ST_Distance(p1, p2, 'spherical') 显式声明球面模式,返回单位为米。

MySQL 8.0+ 怎么用 ST_Distance 查附近的人
直接说结论:ST_Distance 在 MySQL 中默认返回的是**笛卡尔平面距离(单位:米)**,但前提是你的字段类型是 POINT 且 SRID 设为 4326,并显式启用地理模式(ST_Distance(..., ...) 第二个参数为 geography 模式才按球面算)。而 MySQL 8.0.17+ 才真正支持地理空间距离计算;老版本哪怕字段设了 SRID 4326,ST_Distance 仍按平面投影算,结果偏差可能达几百米甚至几公里。
实操建议:
- 确认 MySQL 版本 ≥ 8.0.17:
SELECT VERSION(); - 确保经纬度字段是
POINT类型,且显式指定 SRID:POINT(116.48 39.92) SRID 4326 - 查询时用
ST_Distance(p1, p2, 'spherical')显式声明球面计算(MySQL 8.0.29+ 支持该语法),否则默认仍是平面 - 注意:即使用了球面模式,
ST_Distance返回单位仍是「米」,不是度数
PostgreSQL + PostGIS 的 ST_Distance 为什么经常返回 0 或极小值
常见现象:明明两个点经纬度差挺大,ST_Distance 却返回 0、0.0001 或几十米——大概率是坐标系没对齐。PostGIS 默认把 geometry 当平面处理(单位是“度”),而你传入的 WGS84 经纬度(SRID 4326)本质上是球面坐标,直接算欧氏距离毫无地理意义。
正确做法:
- 用
geography类型而非geometry:把字段定义为geography(POINT, 4326),或查询时强转:ST_SetSRID(ST_MakePoint(lng, lat), 4326)::geography - 函数必须配对使用:
ST_Distance(geog1, geog2),不能混用geometry和geography - 避免写
ST_Distance(a.geom, b.geom)—— 这是在算“度”为单位的平面距离,数值不可读 - 如果非要走
geometry路线,得先用ST_Transform投影到米制坐标系(如 EPSG:3857 或本地 UTM),再算距离
为什么加了 WHERE ST_Distance(...) 查询还是慢
核心问题:ST_Distance 是标量函数,无法利用空间索引加速范围过滤。即使你建了 GIST(PostGIS)或 SPATIAL(MySQL)索引,优化器也无法下推这个条件。
必须改用空间谓词提前剪枝:
- PostgreSQL:用
ST_DWithin(geog1, geog2, 1000)(单位米),它会自动利用geography索引 - MySQL:用
ST_Within(p, ST_Buffer(中心点, 半径))不推荐(ST_Buffer效率低);更可靠的是先用ST_Intersects(p, ST_Envelope(...))配合矩形粗筛,再用ST_Distance精筛 - 通用技巧:先用经纬度范围快速过滤(如
lng BETWEEN x-0.01 AND x+0.01),再在子查询里调ST_Distance,能显著减少计算量
SQL 计算附近人时最容易被忽略的精度陷阱
不是所有“1000 米内”的人,都真的在 1000 米球面距离内——尤其在高纬度地区。比如哈尔滨(北纬 45°)和赤道附近,同样经度差 0.01°,实际东西向距离相差约 30%。而 ST_Distance 虽然算球面,但底层用的是球体模型(WGS84 是椭球),MySQL 目前只支持球体近似,PostGIS 默认用更准的椭球算法(use_spheroid=true 是默认行为)。
所以实际部署时要注意:
- MySQL 用户别依赖
ST_Distance做高精度风控类判断(比如“是否进入电子围栏”),误差可能超 10 米 - PostGIS 用户若发现距离偏小,检查是否误关了椭球计算:
ST_Distance(geog1, geog2, false)会强制用球体,应删掉第三个参数 - 前端传来的经纬度常有 5~6 位小数,但 GPS 实际精度往往只有 3~5 米,过度追求亚米级计算反而误导业务逻辑

















