sql - 两个坐标之间的距离,我怎样才能简化这个和/或使用不同的技术?

标签 sql mysql coordinates geospatial distance

我需要编写一个查询,使我能够从提供的位置找到一定范围(英里)内的所有位置。

表格是这样的:

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 将可能的键显示为 latlngPRIMARY 并使用了 PRIMARY.

我怎样才能做得更好???

我知道我可以将经纬度存储为 POINT();但我也没有找到太多关于此的文档来显示它是更快还是更准确?

我们很乐意接受任何其他想法!

非常感谢!

-斯特凡


更新:

正如 Jonathan Leffler 所指出的,我犯了一些我没有注意到的错误:

我只将 abs() 放在其中一个纬度值上。当不需要时,我也在第二个 WHERE 子句中使用了 id 搜索。第一个查询纯粹是实验性的,第二个查询更有可能投入生产。

在这些更改之后,EXPLAIN 显示 key 现在使用 lng 列并且平均响应时间现在在 180ms 左右,这是一个改进。

最佳答案

我们很乐意接受任何其他想法!

如果您想要速度(和简单性),您需要数据库提供一些不错的地理空间支持。这介绍了地理空间数据类型、地理空间索引和(许多)用于处理/构建/分析地理空间数据的函数。

MySQL implements a part of the OpenGIS specifications尽管它/曾经(上次我检查它是)边缘非常非常粗糙/过早(对任何实际工作都没有用)。

PostGisPostgreSql将使这变得简单易读:

(这会找到来自 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/

相关文章:

sql - 如果开始日期在一个月 SQL 中为 'Week Number',如何从日期中提取 'Day Number' 和 'Monday'

sql - 如何在 SQL 中对数组进行 string_agg?

c# - 如何将框的边界转换为屏幕坐标

Mysql分组计数日期范围内的数据行

mysql - 如何查明 my.cnf 正在使用什么?

mysql - SQL 2 列主键,但想要搜索其中一列,我应该将其设为单独的键吗?

mysql - SELECT MAX(...) 在存储过程中错误地返回 NULL

android - 如何在动画之后将更改应用到 View 位置?

c++ - 在 C++ 中修改 3D 坐标

mysql - 使用MYSQL计算同一列中多个日期之间的时间