两个纬度/经度表之间的距离计算
Posted
技术标签:
【中文标题】两个纬度/经度表之间的距离计算【英文标题】:distance calculation between two tables of lat/lon 【发布时间】:2018-06-11 16:38:36 【问题描述】:我有以下两张表
城市
id,纬度,经度
山
id,纬度,经度
SELECT cities.id,
(SELECT id FROM mountains
WHERE SQRT(POW(69.1 * ( latitude - cities.lat ) , 2 ) +
POW( 69.1 * (cities.lon - longitude ) *
COS( latitude / 57.3 ) , 2 ) )<20 LIMIT 1) as mountain_id
FROM cities
(查询耗时 0.5060 秒。)
为了复杂性,我删除了查询的某些部分(例如 order by、where)。但是它并不真正影响执行时间。
下面的解释。
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY cities ALL NULL NULL NULL NULL 478379
2 DEPENDENT SUBQUERY mountains ALL NULL NULL NULL NULL 15645 Using where
使用 SELECT 本身不是我的问题,但是当我尝试使用给定的结果时......例如
id mountain_id
588437 NULL
588993 4269
589014 4201
589021 4213
589036 4952
589052 7625
589113 9235
589125 NULL
589176 1184
589210 4317
...更新表一切都变得非常缓慢。我几乎尝试了我所知道的一切。我确实知道依赖子查询不是最优的,但我不知道如何摆脱它。
有什么方法可以改进我的查询。也许将其更改为 JOIN?
这两个表本身没有任何共同点,只是纬度和经度不同,只有在使用计算时才会相互关联。
MariaDB 中的空间距离搜索(公里、英里)似乎尚不可用。
【问题讨论】:
您似乎想要彼此相距 20 英里以内的所有对(城市/山脉)。对吗? 这就是它现在所做的。 20 英里只是为了减少结果量。当我按距离添加顺序时(为了复杂性,我现在省略了)它给了我从 table1 到 table2 的最接近的匹配。 【参考方案1】:快速进行此类操作的诀窍是避免在每对可能的纬度/经度点上进行所有计算。为此,您应该合并一个边界框操作。
让我们从使用 JOIN 开始。在伪代码中,你想要这样的东西,但如果你捕捉到一些额外的对并不重要,只要它们比其他的相距更远。
SELECT c.city_id, m.mountain_id
FROM cities c
JOIN mountains m ON distance_in_miles(c, m) < 20
因此,我们需要弄清楚如何使 ON 子句快速运行——让它使用索引,而不是在所有城市和山脉中漫无目的(向 Woody Guthrie 道歉)。
让我们在 ON 子句中试试这个。它会在 +/- 20 英里的方形边界框内搜索附近的配对。
SELECT c.city_id, m.mountain_id
FROM cities c
JOIN mountains m
ON m.lat BETWEEN c.lat - (20.0 / 69.0)
AND c.lat + (20.0 / 69.0)
AND m.lon BETWEEN c.lon - (20.0 / (69.0 * COS(RADIANS(c.lat))))
AND c.lon + (20.0 / (69.0 * COS(RADIANS(c.lat))))
在此查询中,20.0
是比较限制半径,69.0
是定义每纬度法定英里数的常数。
然后,在两个表的(lat, lon, id)
上放置复合索引,您的JOIN
操作将能够使用索引范围扫描来提高查询效率。
最后,您可以在伪代码中使用这些子句来扩充查询
ORDER BY dist_in_miles (c,m) ASC
LIMIT 1
这里您实际上需要使用距离公式。您问题中的笛卡尔距离公式是一个近似值,除非您靠近极点,否则效果还不错。您可能想改用大圆公式。这些被称为球余弦定律、半正弦或文森蒂公式。
【讨论】:
提前致谢!我试过你的方法。现在看起来要慢得多,但我还没有添加所有过滤器。如何过滤结果以获得每个 city_id 的结果? LIMIT 影响整个结果集。最后,我希望看到我在原始帖子中看到的列表,其中我得到一个 city_id 和一个 mountain_id。以上是关于两个纬度/经度表之间的距离计算的主要内容,如果未能解决你的问题,请参考以下文章