2016-01-21 142 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,但可以更改。