登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

MySQL GIS ST_Distance_Sphere怎样计算经纬度近似距离

来源:17golang原创

时间:2026-11-04 08:41:58 148浏览 收藏

做“附近门店”“配送范围”或“两个坐标相距多远”时,MySQL GIS 的 ST_Distance_Sphere() 可以直接给出球面上的近似距离。它返回的是米,不是公里;对经纬度点来说,最容易出错的地方也不是函数名,而是把纬度和经度写反、误读默认半径,或者把近似距离当成高精度测地线结果。

用经纬度计算近似距离的最小写法是:先用经度作为 X、纬度作为 Y 构造相同 SRID 的 Point,再调用 ST_Distance_Sphere(point_a, point_b)。返回值除以 1000 就是公里数。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/

先把函数返回值和输入边界说清楚

MySQL 8.4 文档将 ST_Distance_Sphere(g1, g2 [, radius]) 定义为计算 Point 或 MultiPoint 在球面上的最小距离,结果单位为米。省略第三个参数时,SRID 0 的经纬度 Point 使用 6370986 米作为默认半径。

这里的“经纬度”对应 WKT 中的坐标顺序:X 是经度,Y 是纬度。北京附近的点应写成 POINT(116.3974 39.9093),不能写成 POINT(39.9093 116.3974)。后者仍然像一个合法的点,却表达成了完全不同的位置。

MySQL GIS 中经度作为 X、纬度作为 Y 并输入球面距离函数的静态说明图
图1:操作示意图,展示经度、纬度、Point 和米制距离之间的输入关系;这是原创静态说明图,不是运行截图或执行证据。

用两个 Point 计算米和公里

下面用两个变量表示起点和终点。示例只展示 SQL 结构,坐标值是用于说明的固定样例;在业务代码中应使用参数绑定,不要拼接用户输入。

-- X 写经度,Y 写纬度;两个点都使用 SRID 0
SET @start_point = ST_GeomFromText('POINT(116.3974 39.9093)', 0);
SET @end_point = ST_GeomFromText('POINT(121.4737 31.2304)', 0);

-- 函数返回米;除以 1000 后再四舍五入为公里
SELECT ST_Distance_Sphere(@start_point, @end_point) AS distance_meters,
       ROUND(ST_Distance_Sphere(@start_point, @end_point) / 1000, 2)
         AS distance_kilometers;

同一个距离表达式被调用两次时,阅读上比较直观,但复杂查询中可以先在子查询或应用层保存一次结果。返回值是数值距离,单位转换只改变展示单位,不会改变函数内部使用的球面模型。

从经纬度列生成点,再筛选半径内数据

如果表中分别保存了 longitude 和 latitude,可以在查询中临时构造 Point。下面用 5 公里作为示例半径,并把筛选条件保持为米制,避免把公里和米混用。

-- 门店坐标表中的 longitude 是经度,latitude 是纬度
SET @user_longitude = 116.3974;
SET @user_latitude = 39.9093;
SET @radius_meters = 5000;

-- 先计算距离,再只保留半径内的门店;NULL 坐标不会满足条件
SELECT s.id,
       s.name,
       ST_Distance_Sphere(
         ST_GeomFromText(
           CONCAT('POINT(', s.longitude, ' ', s.latitude, ')'), 0
         ),
         ST_GeomFromText(
           CONCAT('POINT(', @user_longitude, ' ', @user_latitude, ')'), 0
         )
       ) AS distance_meters
FROM store AS s
WHERE s.longitude IS NOT NULL
  AND s.latitude IS NOT NULL
  AND ST_Distance_Sphere(
        ST_GeomFromText(
          CONCAT('POINT(', s.longitude, ' ', s.latitude, ')'), 0
        ),
        ST_GeomFromText(
          CONCAT('POINT(', @user_longitude, ' ', @user_latitude, ')'), 0
        )
      ) 

这段写法重点是表达坐标顺序和单位,并不等于所有大表都应直接全表计算。生产查询通常还要结合经纬度范围预过滤、空间列设计和执行计划,先缩小候选集,再计算精确到当前模型的距离。

SRID、范围和 NULL 不要混着处理

两个几何参数应处于相同的空间参考系。若输入是 SRID 0 的 Point,MySQL 会按经度和纬度的度数解释坐标;若使用地理空间参考系,则默认半径由该 SRS 的平均半径决定。不要只给数字加一个 SRID,就把原始坐标自动变成了另一个坐标系。

经度的常见有效范围是 (-180, 180],纬度应落在地理坐标可接受范围内。输入为空时,空间便利函数通常返回 NULL;格式错误、未定义 SRS 或不同 SRID 则可能抛出 GIS 相关错误。因此,接口层应先校验数字、范围和必填性,数据库层仍需保留必要的约束或异常处理。

-- 在写入或查询前检查范围,避免把合法数字当成合法坐标
SELECT CASE
         WHEN @longitude IS NULL OR @latitude IS NULL THEN 'missing'
         WHEN @longitude  180 THEN 'longitude_out_of_range'
         WHEN @latitude  90 THEN 'latitude_out_of_range'
         ELSE 'ok'
       END AS coordinate_status;

默认半径够用时,不要误称为高精度测距

ST_Distance_Sphere() 的优势是写法直接、返回值容易用于附近查询;它描述的是球面近似距离。地图导航的道路距离、跨复杂地形的实际行驶距离,以及对误差非常敏感的测绘场景,都不能只拿这个值当最终答案。

如果业务需要自定义球面半径,可以显式传入第三个参数,单位仍是米:

-- 自定义球面半径时仍使用米;数值应来自明确的业务或模型约定
SELECT ST_Distance_Sphere(
         ST_GeomFromText('POINT(116.3974 39.9093)', 0),
         ST_GeomFromText('POINT(121.4737 31.2304)', 0),
         6371000
       ) AS distance_meters;

不要为了让结果“看起来更准”随意调整半径。先明确坐标的 SRS、结果允许的误差和下游用途,再决定使用默认半径、自定义半径,还是改用更适合的空间距离函数。

MySQL GIS 距离筛选中米制半径、候选点和近似边界的静态说明图
图2:结果示意图,展示距离值、半径筛选和球面近似边界的关系;这是原创静态说明图,不代表某次线上查询结果。

把 ST_Distance_Sphere 和 ST_Distance 区分开

问题ST_Distance_Sphere判断建议
结果单位米展示公里时再除以 1000
默认模型球面近似附近检索、粗粒度排序可优先考虑
坐标顺序经度 X、纬度 Y构造 WKT 时固定检查
精度预期不是道路或测绘结果高精度业务先核对 SRS 和函数选择
大表成本逐行计算可能昂贵先做候选范围缩小,再做距离计算

我在实际排查这类 SQL 时,通常先看三个值:坐标顺序是否正确、距离单位是否统一、输入点是否处于同一 SRID。三项都成立后,再讨论空间索引和候选集优化,效率会比一开始盯着公式更高。

为什么算出来的距离特别大?

优先检查是否把纬度写在 X、经度写在 Y。还要检查单位是否把米直接展示成公里,以及是否把原始坐标误当成已经转换过的坐标。

能不能直接传经纬度两个数字?

不能把两个数字当作函数的两个坐标参数。先用 ST_GeomFromText、ST_PointFromText 或表中的空间列构造几何对象,再传给距离函数。

为什么两个点的结果是 NULL?

先看任一输入是否为 NULL,再检查几何文本、SRID 和列值范围。返回 NULL 与参数错误不是一回事,应用层应分别记录缺失值和 GIS 异常。

归纳起来,MySQL GIS 计算经纬度近似距离的关键不是记住一条公式,而是建立稳定的输入契约:经度在前、纬度在后;同一 SRID;结果按米处理;近似距离只用于适合的业务边界。官方参考可从 https://dev.mysql.com/doc/refman/8.4/en/spatial-convenience-functions.html 开始。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>