2012-08-01 103 views
4

下午,在小數列上找到MySQL中最接近的匹配

我在解決這個問題時遇到了一些困難。我有一張MySQL表格,其中包含英國郵政編碼及其經度和緯度值列表。我希望能夠在表格上進行搜索,以找到給定長/拉特對的最接近的郵政編碼。

我一直在嘗試使用的查詢是:

"SELECT id, outcode AS thecode, @la := MATCH(lat) AGAINST(?) AS score_lat, @ln := MATCH(lng) AGAINST(?) AS score_lng, @la + @ln AS score_total FROM postcodes ORDER BY score_total DESC LIMIT 10 

然而,這只是返回似乎是隨機的郵政編碼,例如與緯度:55.775549和龍:-4.047556

Array 
(
[0] => Array 
    (
     [id] => 929 
     [thecode] => FK14 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 

[1] => Array 
    (
     [id] => 2785 
     [thecode] => UB3 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 

[2] => Array 
    (
     [id] => 993 
     [thecode] => G70 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 

[3] => Array 
    (
     [id] => 2849 
     [thecode] => WC2B 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 

[4] => Array 
    (
     [id] => 1057 
     [thecode] => GU29 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 

[5] => Array 
    (
     [id] => 2913 
     [thecode] => WS13 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 

[6] => Array 
    (
     [id] => 1121 
     [thecode] => HP20 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 

[7] => Array 
    (
     [id] => 1185 
     [thecode] => IG6 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 

[8] => Array 
    (
     [id] => 1249 
     [thecode] => IV25 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 

[9] => Array 
    (
     [id] => 1313 
     [thecode] => KA8 
     [score_lat] => 0 
     [score_lng] => 0 
     [score_total] => 0 
    ) 
) 

數據庫的架構是:

CREATE TABLE `postcodes` (
    `id` int(11) NOT NULL auto_increment, 
    `outcode` varchar(4) NOT NULL, 
    `lat` varchar(20) NOT NULL, 
    `lng` varchar(20) NOT NULL, 
    PRIMARY KEY (`id`), 
    FULLTEXT KEY `lat` (`lat`), 
    FULLTEXT KEY `lng` (`lng`) 
) ENGINE=MyISAM AUTO_INCREMENT=2975 DEFAULT CHARSET=latin1 AUTO_INCREMENT=2975 ; 

我希望有人能幫助!如果您需要了解更多信息,請只問...

感謝,

tip2tail

+0

你看過嗎:http://dev.mysql.com/doc/refman/5.0/en/spatial-extensions.html你的應用程序相當簡單,因爲它只是距離公式的基本應用程序,但你問的是一個空間問題。你在做什麼試圖匹配字符串,你實際要問的問題是一個距離問題。這種方法永遠不會有效。 – hsanders 2012-08-01 19:03:09

+0

@hsanders我不知道該從哪裏開始!你能提供任何建議或例子,說明我可以如何實現我所需要的?謝謝tip2tail – tip2tail 2012-08-01 19:23:37

回答

5

MySQL的MATCH()功能用於全文搜索到的字符串「匹配」。 (所以它返回零值並不奇怪)

如果用「最接近」的意思,你想要計算出地圖上兩點之間的距離(就像測量'烏鴉'一樣)座標以(十進制度數)經度和緯度給出,您確實需要使用大圓距離(GCD)計算。

http://en.wikipedia.org/wiki/Great-circle_distance

你可以跳過那些血淋淋的細節,只是利用我的實現。下面是我的SQL語句之一的SELECT列表中的節選,該表達式計算兩個點之間的距離(以英里)...

 , ACOS(
      COS(RADIANS(d2.latitude)) 
     * COS(RADIANS(d1.latitude)) 
     * COS(RADIANS(d2.longitude) - RADIANS(d1.longitude)) 
     + SIN(RADIANS(d2.latitude)) 
     * SIN(RADIANS(d1.latitude)) 
      )*3958.82 AS distance_miles 

在這個例子中,d1代表原點,和d2代表目的地點。 latitudelongitude作爲DECIMAL值提供。

對於d1的單個「已知」點,我可以通過此表達式進行排序,以首先獲得「最接近的」d2。 (對於多原點,我可以通過d1.id訂購,然後由這個表達式得到最接近d2先爲每個d1,但有足夠的瞭解我的問題......


我複製從你的問題的查詢和修改。它(如下)基本上,我去掉了「分數」的列,並與做了距離計算的表達式代替它:

SELECT id 
    , outcode AS thecode 
    , ACOS(
      COS(RADIANS(d2.latitude)) 
     * COS(RADIANS(@d1_latitude)) 
     * COS(RADIANS(d2.longitude) - RADIANS(@d1_longitude)) 
     + SIN(RADIANS(d2.latitude)) 
     * SIN(RADIANS(@d1_latitude)) 
      )*3958.82 AS distance_miles 
    FROM postcodes d2 
    JOIN (SELECT @d1_latitude := ?, @d1_longitude := ?) v 
ORDER BY distance_miles LIMIT 10 

在這種情況下@d1_變量(從綁定變量分配)是緯度和「已知」點的經度。對於中的每一行表(爲方便起見,我將其別名爲d2),此表達式計算表中緯度/長度與「已知」點之間的距離。

注意:內嵌視圖別名爲v就在那裏,因此您只能綁定一次緯度,並將值分配給可引用的用戶變量。該內聯視圖可以省略,您可以看到需要將緯度綁定兩次的位置。

注意:這將計算「英里數」中的距離。通過用一個不同的值代替3958.82常數,您可以很容易地獲得以公里(km)爲單位的距離。

注意:沒有必要返回距離;如果您只希望按距離返回距離最近的10個,您可以將該表達式放在ORDER BY子句中,例如,

SELECT id 
    , outcode AS thecode 
    FROM postcodes d2 
    JOIN (SELECT @d1_latitude := ?, @d1_longitude := ?) v 
ORDER 
    BY ACOS(
      COS(RADIANS(d2.latitude)) 
     * COS(RADIANS(@d1_latitude)) 
     * COS(RADIANS(d2.longitude) - RADIANS(@d1_longitude)) 
     + SIN(RADIANS(d2.latitude)) 
     * SIN(RADIANS(@d1_latitude)) 
      )*3958.82 AS distance_miles 
LIMIT 10 

請讓我知道,如果你正在尋找比兩點之間的距離以外的東西,因爲在這種情況下,這個答案是真的沒有對您有所幫助。

+0

@ spencer7592謝謝!我期待閱讀代碼並瞭解如何將其融入到我的項目中。 t2t – tip2tail 2012-08-01 19:30:28

+0

@ spencer7592謝謝,但林不知道我明白。我不知道「目的地」是什麼。我正在嘗試獲取與當時用戶所在地最接近的郵政編碼。所以我只有一個我知道的緯度/長度和我想檢查最接近他們的潛在緯度/長度的列表? – tip2tail 2012-08-01 19:34:59

+0

@每個人 - 我發現了另一種通過第三方API實現此目的的方法。我已經標記你的答案@ spencer7592正確,因爲你非常有幫助。再次感謝! – tip2tail 2012-08-01 19:40:05