MySQL 计算经纬度半径内最近商家
MySQL 计算经纬度半径内最近商家,核心用 Haversine 公式(球面距离计算),结合索引优化提升查询效率,以下是完整实现方案:
一、核心公式(Haversine)
计算两点(用户经纬度 vs 商家经纬度)的球面距离(单位:公里):
6371 * 2 * ASIN(
SQRT(
POWER(SIN((用户纬度 - 商家纬度) * PI() / 180 / 2), 2) +
COS(用户纬度 * PI() / 180) * COS(商家纬度 * PI() / 180) *
POWER(SIN((用户经度 - 商家经度) * PI() / 180 / 2), 2)
)
) AS distance- 6371 是地球平均半径(公里),需米则换为 6371000。
- 经纬度需先转弧度( * PI() / 180 ),避免计算误差。
二、完整查询示例(返回半径 5 公里内最近商家)
表结构假设(商家表 merchant )
CREATE TABLE merchant ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) COMMENT '商家名称', lat DECIMAL(10,6) NOT NULL COMMENT '纬度(如:39.9042)', lng DECIMAL(10,6) NOT NULL COMMENT '经度(如:116.4074)', address VARCHAR(200) COMMENT '地址' );查询 SQL(用户经纬度:39.9042, 116.4074)
SELECT id, name, lat, lng, address, -- 计算距离(公里) 6371 * 2 * ASIN( SQRT( POWER(SIN((39.9042 - lat) * PI() / 180 / 2), 2) + COS(39.9042 * PI() / 180) * COS(lat * PI() / 180) * POWER(SIN((116.4074 - lng) * PI() / 180 / 2), 2) ) ) AS distance FROM merchant -- 筛选 5 公里内(先粗筛,提升效率) WHERE lat BETWEEN 39.9042 - 5 / 111.12 AND 39.9042 + 5 / 111.12 -- 纬度每度约 111.12 公里 AND lng BETWEEN 116.4074 - 5 / (111.12 * COS(39.9042 * PI() / 180)) AND 116.4074 + 5 / (111.12 * COS(39.9042 * PI() / 180)) -- 按距离排序(最近在前) ORDER BY distance ASC -- 限制返回数量 LIMIT 10;
三、性能优化(大数据量必备)
添加空间索引(MySQL 5.7+ 支持)
-- 方式 1:创建经纬度联合索引(基础优化) CREATE INDEX idx_merchant_lat_lng ON merchant(lat, lng); -- 方式 2:创建空间索引(高效,支持 GIS 函数) ALTER TABLE merchant ADD COLUMN location POINT NOT NULL COMMENT '经纬度空间点' AFTER lng; -- 更新空间点数据(lat 纬度,lng 经度,注意顺序) UPDATE merchant SET location = POINT(lng, lat); -- 创建空间索引 CREATE SPATIAL INDEX idx_merchant_location ON merchant(location);空间索引查询(更高效)
SELECT id, name, lat, lng, address, -- ST_Distance_Sphere 直接计算球面距离(米),MySQL 8.0+ 支持 ST_Distance_Sphere(POINT(116.4074, 39.9042), location) / 1000 AS distance -- 转公里 FROM merchant -- 筛选 5 公里内(空间范围查询) WHERE ST_DWithin( location, POINT(116.4074, 39.9042), 5000 -- 半径(米) ) ORDER BY distance ASC LIMIT 10;
四、关键注意事项
1. 经纬度格式:确保存储为 DECIMAL(10,6) (保留 6 位小数,精度约 10 厘米),避免用 FLOAT (精度丢失)。
2. 粗筛逻辑:未用空间索引时,先通过 BETWEEN 筛选大致经纬度范围,减少计算量。
3. 版本支持: ST_Distance_Sphere 是 MySQL 8.0+ 函数,5.7 及以下用 Haversine 公式。
五、应用层整合(如 PHP)
将用户经纬度(前端获取)传入 SQL,示例(Laravel):
$userLat = 39.9042; // 用户纬度(前端定位获取)
$userLng = 116.4074; // 用户经度
$radius = 5; // 搜索半径(公里)
$merchants = DB::table('merchant')
->selectRaw("
id, name, lat, lng, address,
6371 * 2 * ASIN(
SQRT(
POWER(SIN(($userLat - lat) * PI() / 180 / 2), 2) +
COS($userLat * PI() / 180) * COS(lat * PI() / 180) *
POWER(SIN(($userLng - lng) * PI() / 180 / 2), 2)
)
) AS distance
")
->whereRaw("
lat BETWEEN ? - ? / 111.12 AND ? + ? / 111.12
AND lng BETWEEN ? - ? / (111.12 * COS(? * PI() / 180)) AND ? + ? / (111.12 * COS(? * PI() / 180))
", [$userLat, $radius, $userLat, $radius, $userLng, $radius, $userLat, $userLng, $radius, $userLat])
->orderBy('distance')
->limit(10)
->get();
版权属于:Joyber
本文链接:https://blog.qqvbc.com/default/1375.html
转载时须注明出处及本声明