我有以下两个表
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)似乎尚不可用。
答案 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公式。