ST_Distance_Sphere是MySQL 5.7.6+推荐的球面距离计算函数,按WGS84椭球模型自动计算,单位米、精度高;参数须为POINT(longitude, latitude),顺序不可颠倒,且必须用POINT类型。

MySQL中用ST_Distance_Sphere直接算球面距离最可靠
MySQL 5.7.6+ 原生支持地理空间计算,ST_Distance_Sphere是当前最推荐的方式——它自动按WGS84椭球模型计算,结果单位是米,精度高且无需手动写Haversine公式。
常见错误是传入坐标顺序颠倒或类型不对:该函数要求参数是POINT(longitude, latitude),注意是“经度在前、纬度在后”,和日常说的“经纬度”顺序相反;且必须用POINT类型,不能直接传数值。
- 正确写法:
ST_Distance_Sphere(POINT(lng1, lat1), POINT(lng2, lat2)) - 错误写法:
ST_Distance_Sphere(POINT(lat1, lng1), ...)(纬度经度颠倒) - 错误写法:
ST_Distance_Sphere(lng1, lat1, lng2, lat2)(非POINT类型,会报错Invalid GIS data provided)
兼容老版本MySQL(
如果还在用MySQL 5.6或更早版本,ST_Distance_Sphere不可用,只能自己实现Haversine。注意这不是近似估算——只要角度转弧度、sin/cos调用准确,误差通常在0.5%以内。
关键点在于:所有角度必须先转为弧度(* PI() / 180),地球平均半径取6371008.8米(WGS84标准值),不要用6371或6378凑整。
DELIMITER $$
CREATE FUNCTION haversine_distance(
lat1 DOUBLE, lng1 DOUBLE,
lat2 DOUBLE, lng2 DOUBLE
) RETURNS DOUBLE
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE r DOUBLE DEFAULT 6371008.8;
DECLARE phi1 DOUBLE DEFAULT RADIANS(lat1);
DECLARE phi2 DOUBLE DEFAULT RADIANS(lat2);
DECLARE delta_phi DOUBLE DEFAULT RADIANS(lat2 - lat1);
DECLARE delta_lambda DOUBLE DEFAULT RADIANS(lng2 - lng1);
DECLARE a DOUBLE DEFAULT
SIN(delta_phi/2) * SIN(delta_phi/2) +
COS(phi1) * COS(phi2) *
SIN(delta_lambda/2) * SIN(delta_lambda/2);
DECLARE c DOUBLE DEFAULT 2 * ATAN2(SQRT(a), SQRT(1-a));
RETURN r * c;
END$$
DELIMITER ;
ST_Distance不能替代ST_Distance_Sphere
有人试过用ST_Distance计算两点距离,发现结果明显偏小(比如北京到上海只算出几百米)。这是因为ST_Distance默认在平面坐标系(Cartesian)下计算,把经纬度当普通XY坐标用了,完全忽略地球曲率。
即使你把坐标存成POINT,只要没显式指定SRID(如ST_GeomFromText('POINT(116.4 39.9)', 4326)),ST_Distance仍走平面算法。而ST_Distance_Sphere强制使用球面模型,且内置SRID 4326校验。
- 安全做法:始终用
ST_Distance_Sphere,不依赖ST_Distance+ SRID - 隐患操作:
ST_Distance(ST_PointFromText('POINT(...)', 4326), ...)—— 依然无效
性能与索引注意事项
无论用哪种方式,纯函数计算都无法利用B-tree索引加速距离筛选。如果要查“附近5公里的店铺”,别写WHERE haversine_distance(...) < 5000,这会全表扫描。
正确做法是先用矩形范围粗筛(利用lng BETWEEN ... AND ... AND lat BETWEEN ... AND ...),再用距离函数精筛。矩形边界可由中心点±5000 / (111.32 * COS(RADIANS(lat)))(经度方向)和±5000 / 110.574(纬度方向)估算。
另外,ST_Distance_Sphere在MySQL 8.0.16+支持隐式空间索引优化,但前提是字段本身定义为POINT SRID 4326并建了SPATIAL索引——不过目前仍无法跳过函数调用,所以矩形预过滤仍是标配。


















