You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL:使用WHERE NOT EXISTS子句时如何保留邻近标记组中的一个

地图应用中保留邻近标记组单个标记的解决方案

问题背景

在NodeJS地图应用里,需要清理彼此邻近的标记,但要求每组邻近标记中保留一个,确保每个区域都有标记留存。当前的SQL查询仅能保留完全无邻近标记的点,无法满足分组留一的需求。

原查询代码:

var diff = 1.7;

const query = `
SELECT m1.marker_id, m1.longitude, m1.latitude
FROM Marker m1
WHERE NOT EXISTS ( 
    SELECT * FROM Marker m2
    WHERE m1.marker_id != m2.marker_id
    AND m1.latitude <= m2.latitude + ${diff}
    AND m1.latitude >= m2.latitude - ${diff}
    AND m1.longitude <= m2.longitude + ${diff}
    AND m1.longitude >= m2.longitude - ${diff}
)
`;

Marker表结构:

CREATE TABLE `Marker` (
    `marker_id` int unsigned NOT NULL AUTO_INCREMENT, 
    `latitude` decimal(20,15) NOT NULL, 
    `longitude` decimal(20,15) NOT NULL,  
    PRIMARY KEY (`marker_id`),  
    UNIQUE KEY `id_UNIQUE` (`marker_id`)
) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

解决方案

方法1:窗口函数实现(MySQL 8.0+推荐)

利用ROW_NUMBER()窗口函数,将经纬度按diff分桶分组,每组保留一个标记:

var diff = 1.7;

const query = `
SELECT marker_id, longitude, latitude
FROM (
    SELECT 
        m.marker_id, 
        m.longitude, 
        m.latitude,
        ROW_NUMBER() OVER (
            PARTITION BY 
                FLOOR(m.latitude / ${diff}), 
                FLOOR(m.longitude / ${diff})
            ORDER BY m.marker_id ASC
        ) AS row_num
    FROM Marker m
) AS grouped_markers
WHERE row_num = 1;
`;

逻辑说明:

  • FLOOR(m.latitude / ${diff})和FLOOR(m.longitude / ${diff})将经纬度按diff间隔划分成桶,同桶内的标记彼此距离不超过diff;
  • ROW_NUMBER()为每个桶内的标记编号,取row_num=1即保留每组中marker_id最小的标记(可修改ORDER BY规则,比如按创建时间排序)。

方法2:自关联分组(兼容低版本MySQL)

如果你的MySQL版本不支持窗口函数,用自关联筛选出每组中marker_id最小的标记:

var diff = 1.7;

const query = `
SELECT m1.marker_id, m1.longitude, m1.latitude
FROM Marker m1
LEFT JOIN Marker m2 
    ON m1.marker_id > m2.marker_id
    AND m1.latitude <= m2.latitude + ${diff}
    AND m1.latitude >= m2.latitude - ${diff}
    AND m1.longitude <= m2.longitude + ${diff}
    AND m1.longitude >= m2.longitude - ${diff}
WHERE m2.marker_id IS NULL;
`;

逻辑说明:

  • 该查询筛选出没有比自身ID更小且在邻近范围内的标记的点,也就是每组邻近标记中ID最小的那个会被留存,其余被过滤。

补充提示

  • 如果diff代表实际距离(如米),需用Haversine公式计算真实地理距离,避免经纬度度数在不同纬度实际长度不同的误差;
  • 可根据业务需求调整保留标记的规则,比如选择每组中距离区域中心最近的标记,或最新创建的标记。

内容的提问来源于stack exchange,提问作者RyanBuss01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 06:54:46