MySQL查询需求:每10米半径内仅返回一组最新经纬度
MySQL实现10米范围内仅保留最新经纬度数据
要解决这个“10米范围内只留最新经纬度”的需求,核心是先把所有点按10米网格分组,再在每个组里挑最新的记录。下面是具体实现步骤和SQL语句:
核心思路
- 把经纬度转换成米级平面坐标:10米属于极小范围,完全可以把球面近似成平面计算,误差可忽略。
- 生成10米网格标识:把米级坐标除以10取整,同一网格内的点都处于10米范围内。
- 按网格分组取最新记录:用窗口函数给每个网格内的记录按时间倒序编号,取编号为1的(即最新的那条)。
假设表结构
假设你的表名为location_data,包含字段:
id:自增主键(可选,辅助判断新旧)latitude:纬度(数值类型,比如DECIMAL(10,8))longitude:经度(数值类型,比如DECIMAL(11,8))created_at:记录创建时间(用来判断最新,无此字段则用id替代)
完整查询语句
WITH location_groups AS ( SELECT id, latitude, longitude, created_at, -- 计算经度方向的10米网格标识 FLOOR( (longitude * 111139 * COS(RADIANS(latitude))) / 10 ) AS grid_x, -- 计算纬度方向的10米网格标识 FLOOR( (latitude * 111139) / 10 ) AS grid_y, -- 每个网格内按时间倒序编号,最新记录排第1 ROW_NUMBER() OVER ( PARTITION BY grid_x, grid_y ORDER BY created_at DESC ) AS rn FROM location_data ) -- 只保留每个网格的最新记录 SELECT id, latitude, longitude, created_at FROM location_groups WHERE rn = 1;
关键部分解释
- 网格计算:
- 纬度每度约等于111139米,直接乘111139转成米级坐标。
- 经度每度的长度随纬度变化,公式为
111139 * COS(RADIANS(latitude)),转成米后再计算网格。 FLOOR()取整确保相邻10米内的点会被分到同一个网格。
- 窗口函数
ROW_NUMBER():PARTITION BY grid_x, grid_y表示按网格分组。ORDER BY created_at DESC让组内最新记录排在最前面,编号为1。
- 结果筛选:
WHERE rn = 1仅保留每个网格的最新记录。
无时间字段的替代方案
如果表没有created_at,用自增主键id判断新旧(假设id越大越新),只需修改排序字段:
WITH location_groups AS ( SELECT id, latitude, longitude, FLOOR( (longitude * 111139 * COS(RADIANS(latitude))) / 10 ) AS grid_x, FLOOR( (latitude * 111139) / 10 ) AS grid_y, ROW_NUMBER() OVER ( PARTITION BY grid_x, grid_y ORDER BY id DESC ) AS rn FROM location_data ) SELECT id, latitude, longitude FROM location_groups WHERE rn = 1;
内容的提问来源于stack exchange,提问作者subhashish
相关产品推荐
相关产品推荐

