SQL 实战:基于经纬度的距离计算与位置查询

发布于:2025-02-11 ⋅ 阅读:(63) ⋅ 点赞:(0)

在位置服务(LBS)系统中,基于地理位置查询和距离计算是核心功能之一。例如:

  • 查找附近的商铺、加油站或医院。
  • 计算两点之间的实际直线距离。
  • 筛选出指定范围内的用户或设备位置。

MySQL 提供了多种方式实现地理位置查询,包括 ST_Distance_Sphere() 和经典的 Haversine 公式。本文将介绍如何使用这两种方式在 MySQL 中进行距离计算与位置筛选。


一、经纬度距离计算原理

1. 地球模型与球面距离

在这里插入图片描述


二、方法 1:使用 ST_Distance_Sphere() 计算球面距离

函数介绍
  • ST_Distance_Sphere(point1, point2) 直接返回两点之间的球面距离,结果以米为单位。
  • POINT(lng, lat) 将经纬度转换为点对象。

案例 1:计算两点之间的直线距离

需求
计算从北京天安门到上海外滩的直线距离。

天安门坐标(39.9087, 116.3975)
外滩坐标(31.2335, 121.4920)


SQL 实现
SELECT ST_Distance_Sphere(  
    POINT(116.3975, 39.9087),  
    POINT(121.4920, 31.2335)  
) AS distance_meters;

查询结果
distance_meters
1064695.78

解释

  • 结果显示北京到上海外滩的直线距离约为 1064 公里。
  • POINT(lng, lat) 将经纬度转换为地理点,ST_Distance_Sphere 计算两点的球面距离。


案例 2:查询当前位置 5 公里范围内的商铺

需求
查询距离当前定位(广州塔 23.1056, 113.32485 公里范围内的商铺信息。


表结构 shops
shop_id name lat lng
1 星巴克 23.1100 113.3300
2 肯德基 23.0920 113.3100
3 麦当劳 23.1500 113.3700
4 德克士 23.0900 113.2500

SQL 实现
SELECT shop_id, name,  
       ST_Distance_Sphere(POINT(lng, lat), POINT(113.3248, 23.1056)) AS distance_meters  
FROM shops  
WHERE ST_Distance_Sphere(POINT(lng, lat), POINT(113.3248, 23.1056)) <= 5000  
ORDER BY distance_meters;

查询结果
shop_id name distance_meters
1 星巴克 566.43
2 肯德基 1887.29

解释

  • 查询范围限定为 5000 米(5 公里)。
  • 使用 WHERE 过滤距离条件,同时按照距离升序排序,方便查看最近的商铺。


三、方法 2:Haversine 公式计算距离

在不支持 ST_Distance_Sphere 的 MySQL 版本中,可以使用 Haversine 公式 实现经纬度距离计算。


案例 3:Haversine 公式计算两点间距离

SQL 实现
SELECT  
    6371000 * 2 * ASIN(  
        SQRT(  
            POWER(SIN(RADIANS((39.9087 - 31.2335) / 2)), 2) +   
            COS(RADIANS(39.9087)) * COS(RADIANS(31.2335)) *  
            POWER(SIN(RADIANS((116.3975 - 121.4920) / 2)), 2)  
        )  
    ) AS distance_meters;

结果

distance_meters
1064695.78

解释

  • 手动实现 Haversine 公式,使用三角函数计算地球表面的两点距离。
  • 6371000 表示地球平均半径,单位为米。


四、复杂位置查询:筛选指定范围内的对象


案例 4:查询用户 10 公里范围内的餐厅

需求
用户当前位置 (30.5702, 104.0648),查询 10 公里内的餐厅。


表结构 restaurants
rest_id name lat lng
1 火锅店 30.5800 104.0700
2 烧烤店 30.5000 104.0000
3 自助餐厅 30.6100 104.1100
4 小吃店 30.4000 103.9500

SQL 实现
SELECT rest_id, name,  
       6371000 * 2 * ASIN(  
           SQRT(  
               POWER(SIN(RADIANS((30.5702 - lat) / 2)), 2) +   
               COS(RADIANS(30.5702)) * COS(RADIANS(lat)) *  
               POWER(SIN(RADIANS((104.0648 - lng) / 2)), 2)  
           )  
       ) AS distance_meters  
FROM restaurants  
HAVING distance_meters <= 10000  
ORDER BY distance_meters;

查询结果
rest_id name distance_meters
1 火锅店 1200.43
2 烧烤店 7890.10


五、性能优化与注意事项

  1. 建立空间索引
    如果表中存储大量地理位置数据,可以使用 空间索引 提升查询速度:
ALTER TABLE shops ADD SPATIAL INDEX (lat, lng);
  1. 限制结果集大小
    在大数据量环境下,添加 LIMIT 和分页,提高查询效率:
ORDER BY distance_meters LIMIT 10;

六、总结

  • MySQL 提供了两种方式实现基于经纬度的距离计算:
    • ST_Distance_Sphere():直接计算,简单高效,推荐使用。
    • Haversine 公式:适用于不支持 ST_Distance_Sphere 的旧版本。
  • 通过位置查询可以实现商铺筛选、附近用户查找等功能,广泛应用于 LBS 场景。

网站公告

今日签到

点亮在社区的每一天
去签到