2012-08-01 106 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