表格是这样的:
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;
这个返回的行在50ms左右,非常好!
然而,随着行的增加,这将显着减慢.
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,这不是太糟糕,但比上一次要慢.但我可以想象,在更高的行数下,这会更快.但是,anEXPLAIN将可能的键显示为lat,lng或PRIMARY并使用PRIMARY.
我怎么能这样做更好???
我知道我可以将lat lng存储为POINT();但是我也没有找到太多关于此的文件,这表明它是否更快或更准确?
任何其他想法都会被愉快地接受!
非常感谢!
-Stefan
更新:
正如Jonathan Leffler所指出的那样,我犯了一些我没有注意到的错误:
我只将abs()放在其中一个lat值上.当我没有需要时,我也在第二个WHERE子句中使用id搜索.在第一个查询纯粹是实验性的,第二个查询更有可能达到生产.
在这些更改之后,EXPLAIN显示密钥现在使用lng列,平均时间现在响应180ms左右,这是一个改进.
解决方法
如果您想要速度(和简单性),您需要从数据库获得一些不错的地理空间支持.这引入了地理空间数据类型,地理空间索引和(许多)处理/构建/分析地理空间数据的功能.
MysqL implements a part of the OpenGIS specifications虽然它/是(上次我检查过它)非常非常粗糙的边缘/过早(对任何实际工作都没用).
PostGis在PostgreSql将使这个简单易读:
(这可以从tableb中找到距离距离表格中的a点1000米的所有点,其中id为123)
select myvalue from tablea,tableb where st_dwithin(tablea.the_geom,tableb.the_geom,1000) and tablea.id = 123