我需要编写一个查询,使我能够从提供的位置找到一定范围(英里)内的所有位置。
表格是这样的:
id | name | lat | lng
于是我一直在研究,发现:this my sql presentation
我已经在大约有 100 行的表格上对其进行了测试,而且还会有更多! - 必须是可扩展的。
我首先尝试了一些更简单的方法:
//just some test data this would be required by user input
set @orig_lat=55.857807; set @orig_lng=-4.242511; set @dist=10;
SELECT *, 3956 * 2 * ASIN(
SQRT( POWER(SIN((orig.lat - abs(dest.lat)) * pi()/180 / 2), 2)
+ COS(orig.lat * pi()/180 ) * COS(abs(dest.lat) * pi()/180)
* POWER(SIN((orig.lng - dest.lng) * pi()/180 / 2), 2) ))
AS distance
FROM locations dest, locations orig
WHERE orig.id = '1'
HAVING distance < 1
ORDER BY distance;
这在 50 毫秒 左右返回了行,这非常好! 然而,随着行数的增加,速度会急剧下降。
EXPLAIN
显示它仅使用 PRIMARY 键,这很明显。
然后看完文章linked above .我试过这样的事情:
// defining variables - this when made into a stored procedure will call
// the values with a SELECT query.
set @mylon = -4.242511;
set @mylat = 55.857807;
set @dist = 0.5;
-- calculate lon and lat for the rectangle:
set @lon1 = @mylon-@dist/abs(cos(radians(@mylat))*69);
set @lon2 = @mylon+@dist/abs(cos(radians(@mylat))*69);
set @lat1 = @mylat-(@dist/69);
set @lat2 = @mylat+(@dist/69);
-- run the query:
SELECT *, 3956 * 2 * ASIN(
SQRT( POWER(SIN((@mylat - abs(dest.lat)) * pi()/180 / 2) ,2)
+ COS(@mylat * pi()/180 ) * COS(abs(dest.lat) * pi()/180)
* POWER(SIN((@mylon - dest.lng) * pi()/180 / 2), 2) ))
AS distance
FROM locations dest
WHERE dest.lng BETWEEN @lon1 AND @lon2
AND dest.lat BETWEEN @lat1 AND @lat2
HAVING distance < @dist
ORDER BY distance;
这次查询的时间在240ms左右,还算不错,但比上次慢了。但我可以想象,如果行数多得多,结果会更快。然而,EXPLAIN
将可能的键显示为 lat
、lng
或 PRIMARY
并使用了 PRIMARY
.
我怎样才能做得更好???
我知道我可以将经纬度存储为 POINT();但我也没有找到太多关于此的文档来显示它是更快还是更准确?
我们很乐意接受任何其他想法!
非常感谢!
-斯特凡
更新:
正如 Jonathan Leffler 所指出的,我犯了一些我没有注意到的错误:
我只将 abs() 放在其中一个纬度值上。当不需要时,我也在第二个 WHERE 子句中使用了 id 搜索。第一个查询纯粹是实验性的,第二个查询更有可能投入生产。
在这些更改之后,EXPLAIN
显示 key 现在使用 lng
列并且平均响应时间现在在 180ms 左右,这是一个改进。
最佳答案
我们很乐意接受任何其他想法!
如果您想要速度(和简单性),您需要数据库提供一些不错的地理空间支持。这介绍了地理空间数据类型、地理空间索引和(许多)用于处理/构建/分析地理空间数据的函数。
MySQL implements a part of the OpenGIS specifications尽管它/曾经(上次我检查它是)边缘非常非常粗糙/过早(对任何实际工作都没有用)。
PostGis在 PostgreSql将使这变得简单易读:
(这会找到来自 tableb 的所有点,这些点距离 tablea 中的点 a 更近 1000 米,id 为 123)
select
myvalue
from
tablea, tableb
where
st_dwithin(tablea.the_geom, tableb.the_geom, 1000)
and
tablea.id = 123
关于sql - 两个坐标之间的距离,我怎样才能简化这个和/或使用不同的技术?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4741384/