2016-01-21 118 views
0

我有MariaDB,服务器版本:10.0.23-MariaDB,经纬度列(浮点数10,6)加上根据纬度计算出的地理位置列(几何)经度列。MariaDB距离公式最近的200个地方没有半径

我想从一个人中找到最近的200人。中心的人员具有传递给查询的经纬度。有没有办法做到这一点没有半径?所以,如果人口密度很高,半径将会很小。如果人口密度低,那么半径会很大。

有大约400万行,它需要尽可能快。这些行可以先根据它们所在的县进行过滤。有些县超大,人口密度低,其他县是人口密度高的小县。我需要以最快的方式找到最近的200人。

回答

0
SELECT *, ST_DISTANCE(geo_location, POINT(lon, lat)) AS distance 
FROM geotable 
ORDER by distance DESC 
LIMIT 200; 

坏消息是它会很慢,因为st_distance()没有使用空间索引。你应该尝试用最大半径来选择较少的记录来限制查询:

set @dist = 100; 
set @rlon1 = [email protected]/abs(cos(radians(lat))*69); 
set @rlon2 = [email protected]/abs(cos(radians(lat))*69); 
set @rlat1 = lat-(@dist/69); 
set @rlat2 = lat+(@dist/69); 

SELECT *, ST_DISTANCE(geo_location, POINT(lon, lat)) AS distance 
FROM geotable 
WHERE ST_WITHIN(geo_location,ENVELOPE(LINESTRING(point(@rlon1, @rlat1), point(@rlon2, @rlat2)))) 
ORDER by distance DESC 
LIMIT 200; 

或者,如果你有每个国家的多边形坐标,你可以用这个来代替最大半径。

+0

对不起,地理位置列上有一个空间索引。也试图避免haversine。它似乎超级慢。 – stormchaser

+0

我的意思是:即使它在InnodB中定义,ST_DISTANCE()也不使用空间索引。你必须在MYISAM中使用空间索引。 – Nikos

0

小数点后6位足够好(16厘米/0.5英尺),但FLOAT(1.7米/5.6英尺)失去了某些精度。从(M,N)FLOATDOUBLE;你会招致两场比赛,其中一场是浪费。

由于没有“2维”索引,因此在地球上“找到最近的”并不是直接的方法。但是,通过使用一个维度的分区和另一个维度的集群PRIMARY KEY,可以做得很好。

大多数解决方案的真正问题是需要在没有找到有效项目的情况下点击大量磁盘块。事实上,通常超过90%的行被触摸是不需要的。

所有这些都在My lat/lng blog中解决。它会触及大概800行以获得你想要的200个,并且它们将很好地聚集,因此只需要触摸几个块。它不需要在国家进行任何预过滤,但它确实需要对表格进行一些根本性的重构。而且,如果您想区分两个拥抱彼此的人,我建议缩放INT(16毫米/ 5/8英寸) - 度* 10000000。另外,FLOAT将不适用于PARTITIONing; INT会。该链接中的代码使用缩放(2.7米/ 8/8英尺)的MEDIUMINT,但可以更改。