lat / lon两个表之间的距离计算

时间:2018-01-01 20:38:51

标签: mysql performance select subquery mariadb

我有以下两个表

城市

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秒。)

为了复杂性,我删除了查询的某些部分(例如,按顺序排列,在哪里)。但是它确实不会影响执行时间。

下面的 EXPLAIN

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中的空间距离搜索(km,miles)似乎尚不可用。

1 个答案:

答案 0 :(得分:1)

快速进行此类操作的技巧是避免在每对可能的lat / lon点上进行所有计算。为此,您应该包含一个边界框操作。

让我们开始使用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

这里你实际上需要使用距离公式。你问题中的笛卡尔距离公式是一个近似值,除非你靠近极点,否则效果会很好。您可能希望使用大圆公式。这些被称为球形余弦定律,半正弦或Vincenty公式。