st_distance_sphere 返回值单位是米,不是千米或度;常见错误是误将其结果当作千米使用,导致距离过滤偏差。

ST_Distance_Sphere 返回的单位到底是米还是千米?
直接说结论:ST_Distance_Sphere 返回值单位是**米**,不是千米,也不是度。这是最容易误用的一点——有人拿结果除以 1000 再比对地图软件显示的“公里数”,发现差几百米,其实根本不用除,它本来就是米。
常见错误现象:把 ST_Distance_Sphere(a, b) 结果当成千米参与 WHERE 过滤,比如写 WHERE ST_Distance_Sphere(a, b) ,本意是“5 千米内”,实际却成了“5 米内”,查不到任何合理数据。
- MySQL 5.7.6+ 和 8.0+ 均遵循此行为,文档明确写“returns distance in meters”
- 输入点必须是
POINT(longitude latitude)格式,注意是“经度在前、纬度在后”,和常见 GeoJSON/前端 API 的[lat, lng]顺序相反 - 若用
ST_GeomFromText('POINT(116.4 39.9)'),必须确认字符串里是lng lat,否则距离会严重失真
为什么用 ST_Distance_Sphere 而不是 ST_Distance?
ST_Distance 在 MySQL 中默认按平面欧氏距离算(即使字段是地理坐标),结果单位是“度”,毫无地理意义;而 ST_Distance_Sphere 显式调用球面模型(近似 WGS84 椭球),输出真实地面距离(米)。
使用场景很明确:只要你的坐标是 GPS 经纬度(WGS84),且需要“两点间真实地面距离”,就必须用 ST_Distance_Sphere;ST_Distance 只适合投影坐标系(如 Web Mercator 的米单位平面坐标),但 MySQL 对投影支持极弱,基本不用。
- 性能上,
ST_Distance_Sphere比ST_Distance略慢,但差距在毫秒级,可忽略 - 不支持空间索引加速距离计算——即使字段加了
SPATIAL索引,ST_Distance_Sphere仍会全表扫描,需配合ST_Within或边界框预过滤 - 如果只做“是否在 10km 内”这种布尔判断,先用
ST_Distance_Sphere(...) 是最直白写法,别试图手动转成平面近似
怎么避免 POINT 构造时经纬度顺序翻车?
MySQL 的 POINT(x y) 规定 x 是经度(longitude)、y 是纬度(latitude),但很多开发者从 Leaflet、Google Maps 习惯传 [lat, lng],一粘贴就错。
典型错误示例:ST_GeomFromText('POINT(39.9 116.4)') —— 这实际表示东经 39.9°、北纬 116.4°,根本不在地球上。
- 安全写法:显式命名变量,例如
SET @lng = 116.4, @lat = 39.9;,再构造ST_Point(@lng, @lat) - 从表中读取时,确认字段定义顺序,比如
location POINT SRID 4326存的必须是POINT(lng lat) - 用
ST_X(point)和ST_Y(point)检查:对合法北京点,ST_X应返回 ≈116.4,ST_Y应返回 ≈39.9
和 Haversine 手动公式比,ST_Distance_Sphere 有什么坑?
很多人用自定义 Haversine 函数,觉得“可控”,但 ST_Distance_Sphere 其实更准(内部用 Vincenty 近似或优化球面公式),且省去手写三角函数的精度和 NULL 处理问题。
真正要注意的是边界情况:
- 输入点任一为
NULL,结果直接为NULL,不会报错,容易漏查——建议外层用IFNULL(..., 99999999)或加IS NOT NULL条件 - 两点重合时返回 0,没问题;但跨国际日期变更线(如东京 vs 美国西海岸)时,因 MySQL 不自动归一化经度(-180~180),可能算出绕地球远路——需提前用
(lng + 180) % 360 - 180标准化 - SRID 必须为 4326(WGS84),否则行为未定义;建表时务必显式指定:
POINT SRID 4326,不要依赖默认
球面距离计算本身不复杂,难的是坐标顺序、单位、SRID、NULL 处理这四点,踩中任意一个,结果就不可信。











