sql 计算两个点之间之间的距离 mysql

本文介绍了一种使用SQL查询计算两点间地理距离的方法,通过解析纬度(lat)和经度(lon)参数,运用三角函数和地球平均半径进行精确计算。此方法适用于地理信息系统(GIS)和位置服务应用。

昀:
select *,ACOS(SIN((#{lat} * 3.1415) / 180 ) *SIN((substring(points,’,’,-1) * 3.1415) / 180 ) +COS((#{lat} * 3.1415) / 180 ) * COS((substring(points,’,’,-1) * 3.1415) / 180 ) *COS((#{lon} * 3.1415) / 180 - (substring(points,’,’,1) * 3.1415) / 180 ) ) * 6378140 dis
FROM t_store
where active=1
order by dis
limit #{page},#{pageSize};

昀:
lat是代表纬度,lon是代表经度,substring(points,’,’,-1)是纬度,substring(points,’,’,-1)是经度

<think>我们正在讨论计算两个用户之间距离的方法。根据引用内容,主要使用经纬度坐标和Haversine公式来计算地球表面两点间的大圆距离(球面距离)。以下是详细的解答: ### 1. **基本原理** 采用Haversine公式计算地球表面两点距离,该公式考虑地球的球体特性,精度较高且计算稳定。公式如下: $$ a = \sin^2\left(\frac{\Delta\phi}{2}\right) + \cos(\phi_1) \cdot \cos(\phi_2) \cdot \sin^2\left(\frac{\Delta\lambda}{2}\right) $$ $$ c = 2 \cdot \text{atan2}(\sqrt{a}, \sqrt{1-a}) $$ $$ d = R \cdot c $$ 其中: - $\phi_1, \phi_2$ 分别为两点的纬度(弧度制) - $\Delta\phi$ 为纬度差(弧度) - $\Delta\lambda$ 为经度差(弧度) - $R$ 为地球半径(常用6371km或6371000m) ### 2. **Java实现方法** 根据引用[1]的代码,实现步骤如下: ```java /** 地球半径常量(单位:米) */ private static final double EARTH_RADIUS = 6371000; // 赤道半径 /** 将角度转换为弧度 */ private static double rad(double d) { return d * Math.PI / 180.0; } /** * 计算两个经纬度坐标的直线距离 * @param lon1 第一个的经度 * @param lat1 第一个的纬度 * @param lon2 第二个的经度 * @param lat2 第二个的纬度 * @return 两点距离(单位:米) */ public static double getDistance(double lon1, double lat1, double lon2, double lat2) { double radLat1 = rad(lat1); double radLat2 = rad(lat2); double a = radLat1 - radLat2; double b = rad(lon1) - rad(lon2); double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))); s = s * EARTH_RADIUS; s = Math.round(s * 10000) / 10000.0; // 四舍五入保留4位小数 return s; } ``` ### 3. **数据库查询优化** 当需要直接从数据库筛选附近用户时,可采用SQL计算距离(引用[3]提供示例): ```sql SELECT id, user_id, ROUND( 6378.138 * 2 * ASIN( SQRT( POW(SIN(('中心纬度' * PI() / 180 - latitude * PI() / 180) / 2), 2) + COS('中心纬度' * PI() / 180) * COS(latitude * PI() / 180) * POW(SIN(('中心经度' * PI() / 180 - longitude * PI() / 180) / 2), 2) ) ), 2 ) AS distance_km FROM user_coordinates HAVING distance_km <= 5 -- 筛选5公里内的用户 ORDER BY distance_km ASC; ``` ### 4. **关键注意事项** 1. **单位统一**: - 经纬度需统一为十进制度(如:116.404, 39.915) - 距离单位:Java代码返回米,SQL示例返回千米 2. **性能优化**: - 数据库预先计算并缓存距离(如定期更新用户距离索引) - 添加空间索引(MySQL的`SPATIAL INDEX`或PostGIS的`GIST`索引)[^4] 3. **误差来源**: - 地球并非完美球体(赤道半径略大于极半径) - 海拔差异未纳入计算(适用多数平面场景) ### 5. **扩展方案** 对于高精度要求(如地质勘探),可: - 使用Vincenty公式(精度更高但计算复杂) - 集成GIS库(如GeoTools) - 调用第三方API(如高德/腾讯地图的路径规划接口) §§相关问题§§ 1. 如何避免海量用户坐标查询时的性能瓶颈? 2. 除了直线距离,如何计算实际导航距离? 3. 不同坐标系(如WGS-84、GCJ-02)转换对距离计算的影响? </think>### 计算用户间距离的方法详解(基于经纬度) #### 1. **核心算法:Haversine公式** 这是计算地球表面两点最短距离(大圆距离)的标准方法,误差在0.3%以内[^1][^2]。公式推导如下: $$ a = \sin^2\left(\frac{\Delta\phi}{2}\right) + \cos(\phi_1) \cdot \cos(\phi_2) \cdot \sin^2\left(\frac{\Delta\lambda}{2}\right) $$ $$ c = 2 \cdot \text{atan2}(\sqrt{a}, \sqrt{1-a}) $$ $$ d = R \cdot c $$ 其中: - $\phi$ = 纬度(弧度制) - $\lambda$ = 经度(弧度制) - $R$ = 地球半径(6371km或6371000m) - $\Delta\phi = \phi_2 - \phi_1$ - $\Delta\lambda = \lambda_2 - \lambda_1$ #### 2. **Java完整实现** ```java // 地球赤道半径(单位:米) private static final double EARTH_RADIUS = 6371000; public static double calculateDistance( double user1Lon, double user1Lat, double user2Lon, double user2Lat ) { // 角度转弧度 double radLat1 = Math.toRadians(user1Lat); double radLat2 = Math.toRadians(user2Lat); double radLonDiff = Math.toRadians(user2Lon - user1Lon); // Haversine公式计算 double a = Math.sin((radLat2 - radLat1)/2) * Math.sin((radLat2 - radLat1)/2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.sin(radLonDiff/2) * Math.sin(radLonDiff/2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // 返回距离(米),保留两位小数 return Math.round(EARTH_RADIUS * c * 100.0) / 100.0; } ``` #### 3. **数据库直接计算MySQL示例)** 在SQL查询中实时计算距离(引用[3]优化版): ```sql SELECT user_id, ROUND(6371000 * 2 * ASIN( SQRT( POW(SIN((RADIANS(:userLat) - RADIANS(latitude))/2), 2) + COS(RADIANS(:userLat)) * COS(RADIANS(latitude)) * POW(SIN((RADIANS(:userLon) - RADIANS(longitude))/2), 2) ) ), 2) AS distance_meters FROM user_locations HAVING distance_meters <= 5000 -- 筛选5公里内用户 ORDER BY distance_meters ASC; ``` #### 4. **关键注意事项** 1. **单位一致性**: - 经纬度需统一为十进制度(非度分秒) - 距离单位:米(6371000)或千米(6371) 2. **性能优化**: ```sql -- 创建预计算列(MySQL 8.0+) ALTER TABLE user_locations ADD COLUMN geo_point POINT SRID 4326, ADD SPATIAL INDEX(geo_point); -- 空间查询(效率提升10倍+) SELECT ST_Distance_Sphere(geo_point, ST_SRID(POINT(:userLon, :userLat), 4326)) FROM user_locations; ``` 3. **误差修正**: - 高精度场景用Vincenty公式(误差0.5mm) - 考虑海拔差异需叠加高度修正项 #### 5. **典型应用场景** - 社交APP的"附近的人" - 外卖/快递的配送距离计算 - 地理围栏(Geofencing)检测 - 疫情密接者追踪系统 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值