MySQL为每行选择N个最近的邮政编码

时间:2015-02-17 21:17:36

标签: mysql

我有一张满是邮政编码的表格。例如:

------------------------------
| zip | latitude | longitude |
------------------------------
|00001| 35.34543 | -76.34234 |
|00002| 43.23423 | -80.32423 |
...
|00008| 24.34543 | -20.53454 |
------------------------------

对于每一行,我想选择最近的n个邮政编码。但是,我似乎只能弄清楚如何选择最近的单个邮政编码。我如何针对所有邮政编码进行调整:

SELECT
  zip, (
    3959 * acos (
      cos ( radians(78.3232) )
      * cos( radians( latitude ) )
      * cos( radians( longitude ) - radians(65.3234) )
      + sin ( radians(78.3232) )
      * sin( radians( latitude ) )
    )
  ) AS distance
FROM zipcodes
ORDER BY distance
LIMIT 0 , 20;

这将选择20个关闭的邮政编码,但我需要将其应用于每一行。我怎么能一次为整个桌子做这个?

1 个答案:

答案 0 :(得分:0)

在阅读this后,我认为你可以使用Haversine公式来计算两点之间的距离,给出每个点的纬度和经度:

以下是一个例子:

select zc.*
     -- First, convert the latitude and longitude to radians:
     , @lat1 := radians(@latitude) as lat1_rad  
     , @lon1 := radians(@longitude) as lon1_rad
     , @lat2 := radians(zc.latitude) as lat2_rad
     , @lon2 := radians(zc.longitude) as lon2_rad
     -- Calculate the differences in latitude and longitude:
     , @delta_lat := @lat2 - @lat1 as delta_lat
     , @delta_lon := @lon2 - @lon1 as delta_lon
     -- The Haversine Formula:
     , @a := pow(sin(@delta_lat / 2), 2) + cos(@lat2) * cos(@lat1) * pow(sin(@delta_lon / 2), 2) as a
     , @c := 2 * atan2(sqrt(@a), sqrt(1 - @a)) as c
     , @d := @R * @c as d -- Distance (Km)
from 
    (select @R := 6371  -- The radius of the earth (Km)
          , @latitude := 65.3234    -- Latitude of the initial point
          , @longitude := -78.3232  -- Longitude of the initial point
    ) as init,
    zip_codes as zc
-- Add any WHERE conditions and/or ORDER
;

如果你想把它放在一个函数中:

delimiter $$
create function haversine_distance(latitude1 double, longitude1 double
                                 , latitude2 double, longitude2 double)
returns double
-- Input: Latitude and longitude of the points you want to calculate,
          given in degrees
begin
    declare lat1, lon1, lat2, lon2 double;
    declare delta_lat, delta_lon double;
    declare a, c, d double;
    declare R double default 6371; -- The radius of the Earth
    -- Convert the inputs to radians
    set lat1 = radians(latitude1);
    set lon1 = radians(longitude1);
    set lat2 = radians(latitude2);
    set lon2 = radians(longitude2);
    -- Calculate the differences between latitudes and longitudes
    set delta_lat = lat2 - lat1;
    set delta_lon = lon2 - lon1;
    -- The Haversine formula
    set a = pow(sin(@delta_lat / 2), 2) +
            cos(lat2) * cos(lat1) * pow(sin(delta_lon / 2), 2);
    set c = 2 * atan2(sqrt(a), sqrt(1 - 1);
    set d = R * c;
    return d;
end $$
delimiter ;

最后,您可以使用此函数计算两点之间的距离,并过滤最近的n。我将使用用户变量来定义原点的纬度和经度:

set @latitude1 = 65.3234, @longitude1 = -78.3232;
set @n = 5; -- The number of nearest points
select zc.*
     , haversine_distance(@latitude1, @longitude1, 
                          zc.latitude, zc.longitude) as distance_km
from zip_codes as zc
order by distance_km
limit @n;