2011-12-08 112 views
1

不知道如何建立這樣的開放給專家:查詢距離

我有一個客戶列表中的客戶正在尋找他們的位置半徑內的事件。我可以存儲他們的郵政編碼(或lat/lng)以及他們將參加活動的最大距離。因此,列lat,lng,距離(例如,lat = '22.7447858',lng ='-82.1398589',距離= 25)。

整天發佈活動並存儲其zipcode/lat/lng。

我想運行一個查詢(每天一次)獲取事件的客戶。我在查看Cyber​​Junkies後Mysql within distance query,但問題是,我正在運行查詢相反的方向。我需要找到那些「圈子距離」涵蓋當前事件的客戶,而不是相反。不知道如何存儲圓距(3列以上是否足夠好或有更好的方法來存儲這種類型的查詢的數據)?不確定如何爲每個事件查詢客戶。

在此先感謝!

回答

2

我覺得有這樣做的兩種主要方法是:在運行計算距離,並預先計算距離一次,然後將它們存儲在查找表中。

選項1,即時計算。 Tom van der Woerdt的回答很好地解釋了你將如何做到這一點。僞代碼查詢是這樣的:

SELECT * FROM customer, event WHERE (<calc distance>) < customer.distance 

選項2,預先計算所有的距離。您將創建一個表格(在此示例中稱爲distance),該表格存儲每個客戶與每個事件之間的距離。它將有三列:customerid,eventidmiles(或任何你想要的距離度量)。循環查看每位顧客計算每個活動的距離並將每個活動存儲在distance中。每次添加新的客戶或活動時,都會將相應的記錄添加到distance表中。一旦這種結構是到位,調查活動將是簡單的:

SELECT * FROM distance WHERE miles < [[some number you pick]] 

那麼哪一個更好?這是CPU時間和磁盤空間之間的折中,所以答案取決於您的資源。選項1(即時計算)將需要DBMS進行更多的工作(更多的CPU時間)。隨着人員和事件的增加,該查詢將花費更長的時間運行。選項2(預先計算的距離)將使查找速度非常快,但折衷是必須將所有這些預先計算的距離存儲在磁盤上。你還需要努力確保你的查詢表是最新的。每當客戶或事件被添加,刪除或其經緯度發生改變時,您都需要相應地更新查找表。 Triggers可以幫助您自動完成此過程;只要確保你嘗試測試每個場景(添加,刪除,移動)以確保查找表得到了它應有的更新。

簡短回答:如果您的數據庫負載非常輕或者磁盤空間有限,選擇選項1(即時計算)。如果您的負載較重但磁盤空間充裕,請選擇選項2。選項2是更可能的情況,它更具可擴展性。

+0

對於2來說,第二個缺點就是無論何時添加新的事件/客戶,您都必須維護或重新生成該列表。 – xQbert

+0

@xQbert你說得對,我沒有說清楚。我會再補充一點。 – ean5533

0

你想要的是:

SELECT * FROM customer, event WHERE (<calc distance>) < customer.distance 

這將只是獲得所有客戶的所有事件,將它們組合在一起,以獲得所有可能的組合(100個客戶和10個事件給出了1000名的組合),然後檢查他們是否」重新在範圍內。 *

我個人建議製作一個DISTANCE(customer,event)函數來爲你計算它。以這種方式管理查詢比較容易,您可以重新使用它。

* 不一定按照這個順序

0

從A點到B點的距離將是一樣的距離B點到A點(除非你在處理道路的方向和不同的路徑) 。

基本上,你會做(在SQL僞代碼)

SELECT distance(event_loc, user_loc) <= user_max_distance 
0

如果你的距離計算類似於那些在this solution,那麼你可以做這樣的事情:

select id1 from Distances 
    join EventTable on id2=EventTable.eventid 
    join UserTable on id1=UserTable.userid 
where type2=<EVENT_TYPE> and type1=<USER_TYPE> 
    and geodistance_km_by_obj(id1,<USER_TYPE>,id2,<EVENT_TYPE>) < UserTable.max_distance 
0

除了你可能會考慮使用笛卡爾座標(x,y和z)其他答案而不是用於db存儲的緯度/經度,因爲生成的查詢表達式在數據庫服務器上的加載/時間比緯度/經度距離的可能查詢更簡單。

一個PHP實現的例子下可以找到:

http://headers-already-sent.com/geodistance/

的方法「getCartesian」將緯度/經度轉換成直角座標系和方法「getDistanceByCartesian」說明了如何計算實際距離。你需要做的是將這個距離計算從PHP轉移到SQL查詢中(這應該不那麼複雜)。

編輯,因爲我發現給一個更實際的例子

基於類,你可以在上面的鏈接我設置了2演示表爲我公司的地點和所有MC多納爾茲餐廳下找到時間在我們的周圍,並轉換緯度/經度從谷歌地圖笛卡爾X,Y,Z:

CREATE TABLE `locations` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `title` varchar(255) NOT NULL DEFAULT '', 
    `lat` double NOT NULL, 
    `lng` double NOT NULL, 
    `x` double NOT NULL, 
    `y` double NOT NULL, 
    `z` double NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

INSERT INTO `locations` (`id`, `title`, `lat`, `lng`, `x`, `y`, `z`) 
VALUES 
    (1,'Ida-Ehre-Platz 10, 20095 Hamburg',53.55053,9.99949,3727600.05477,657242.251356,5124712.81705), 
    (2,'Kieler Straße 191-193, 22525 Hamburg',53.57731,9.93686,3725956.4981,652753.812254,5126481.40905), 
    (3,'Reeperbahn 42, 20359 Hamburg',53.549951,9.964937,3728046.74189,655003.113578,5124674.56664), 
    (4,'Theodor-Heuss-Platz 3, 20354 Hamburg',53.56083,9.99038,3726797.15378,656489.722425,5125393.17725), 
    (5,'Mundsburger Damm 67, 22087 Hamburg',53.57028,10.02642,3725550.98379,658686.623655,5126017.24553), 
    (6,'Paul-Nevermann-Platz 1, 22765 Hamburg',53.552602,9.936678,3728135.78521,653123.397726,5124849.69505), 
    (7,'Friedrich-Ebert-Damm 101, 22047 Hamburg',53.58753,10.08958,3723303.02881,662522.688778,5127156.05819), 
    (8,'Amsinckstraße 73, 20097 Hamburg',53.54271,10.02654,3727978.07563,659123.791421,5124196.16112), 
    (9,'Eiffestraße 440, 20537 Hamburg',53.55214,10.04638,3726919.13256,660267.521487,5124819.17553); 


CREATE TABLE `user` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) NOT NULL DEFAULT '', 
    `lat` double NOT NULL, 
    `lng` double NOT NULL, 
    `x` double NOT NULL, 
    `y` double NOT NULL, 
    `z` double NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

INSERT INTO `user` (`id`, `name`, `lat`, `lng`, `x`, `y`, `z`) 
VALUES 
    (1,'Ministry.BBS, Cremon 36, 20457 Hamburg',53.545943,9.988761,3728127.10678,656615.385203,5124409.77226), 
    (2,'BBS, Dorotheenstraße 60, 22301 Hamburg',53.583231,10.008315,3724617.80169,657307.963226,5126872.28974); 

基於這兩個表的SQL查詢找到一定的距離(2000內的所有位置(餐館),在米在這個例子)到每個用戶(我們公司的辦公室)將是:

SELECT locations.*, 
    2 * 6371000.785 * 
     asin(
      sqrt(
       pow(locations.x - user.x, 2) 
       + pow(locations.y - user.y, 2) 
       + pow(locations.z - user.z, 2) 
      )/(2 * 6371000.785) 
     ) AS distance 
    FROM locations, user 
    HAVING distance < 2000 
    ORDER BY distance ASC 

如果你需要比「米」比你將不得不改變約爲地球半徑的東西。 6371000.785(以米爲單位)以滿足您的需要,並且還可以將所需的2000年的距離更改爲您喜歡的或存儲在用戶表格中的每個用戶的任何內容。