2011-03-07 43 views
8

我有一個擁有4萬個場地的數據庫,現在正在增長。從數據庫中有效地選擇最近的(距離)記錄

假設我是紅點

Easy
我希望能夠儘快找回最接近的紀錄。

但距離太遠,下一個項目可能是任何東西。而且也可能有0-n個匹配。但是當我只是在尋找1時,是否需要加載所有40000個結果? Less obvious

如何根據距離對記錄進行排序?應該在MYSQL還是PHP中完成? 此計算幾乎發生在每個用戶每頁的每個請求上,因此解決方案需要很快。

編輯感謝您的快速和有希望的答案,我需要檢查這些資源,並在幾天內接受/評論答案。

+0

您是否嘗試過在查詢中包含到場地的距離(通過使用計算列),並查看速度變慢了多少? – 2011-03-07 09:25:24

+0

@Sams Holder我已經使用簡單的pythagoran計算查詢了哪些場地靠近路口,腳本執行速度比分配到路口的場地慢1-2秒。 (對於一臺電腦,我覺得這是一個很長的時間) – Moak 2011-03-07 09:31:29

+2

+1做一個很好的圖! – 2011-03-07 09:36:48

回答

3

最簡單的方法是簡單計算每條記錄的距離並按此值排序。問題是:這是非常昂貴的,並且您不能使用該索引。您可以通過僅查看記錄的子集來降低成本,也許可以通過邊界框來限制,正如一些海報在此建議的那樣。

如果您想要一個清晰快速的解決方案,請查看MySQL的Spatial Extensions。這些完全是爲了你想要做的。這些支持:

  • 一個新的塔型「點」
  • 一種特殊的索引類型距離的查詢
  • 的距離運營商進行了優化。

This HOWTO提供了一些例子:

CREATE TABLE address (
    address CHAR(80) NOT NULL, 
    address_loc POINT NOT NULL, 
    PRIMARY KEY(address), 
    SPATIAL KEY(address_loc) 
); 
CREATE TABLE cab (
    cab_id INT AUTO_INCREMENT NOT NULL, 
    cab_driver CHAR(80) NOT NULL, 
    cab_loc POINT NOT NULL, 
    PRIMARY KEY(cab_id), 
    SPATIAL KEY(cab_loc) 
); 

SELECT 
    c.cab_driver, 
    ROUND(GLength(LineStringFromWKB(LineString(AsBinary(c.cab_loc), 
              AsBinary(a.address_loc))))) 
    AS distance 
FROM cab c, address a 
WHERE a.address = 'Foobar street 110' 
ORDER BY distance ASC LIMIT 1; 
+0

請注意,有一個特殊的SPATIAL INDEX與通常的數據庫索引可以利用 – Prasad 2012-05-23 16:16:12

1

按照此article on Movable Type(wi)所述創建一個「邊界框」以用於SQL查詢中的WHERE子句中PHP代碼示例),然後在查詢中包含Haversine公式以計算實際距離,並按距離ASC對結果進行排序。最近的場地將成爲結果集中的第一個回報。

它的邊框,可以幫助你的表現,因爲這意味着你只能做你的數據

的一小部分昂貴的距離計算如果初始查詢不返回任何記錄,拓寬邊界框,然後再次執行查詢,直到獲得響應。

1

除了通過反覆試驗,沒有找到距離的有效方法。也就是說,使用MySQL,您不能通過距離目標的距離對記錄進行排名,然後選擇最上面的記錄。最好的辦法是選擇一個你認爲距離最近的記錄的距離。太大的數字,你會得到太多的記錄,太小的數字,你不會得到任何。假設你選擇40個單位。

WHERE xcoord BETWEEN n - 40 AND n + 40 AND ycoord BETWEEN n - 40 AND n + 40 

現在你已經得到了所有與座標記錄的80×80盒裏面,你的目標爲中心(框會有點歪斜,如果你在經度和緯度工作,但那並不重要)。現在,如果您正在處理經緯度,請使用Haversine方程,或者使用Pythagoras(如果它只是笛卡爾座標)來計算目標與每個點之間的距離。