Я бы просто создал таблицу zip_code_distances и предварительно вычислил расстояния между всеми почтовыми индексами 42K в США, которые находятся в радиусе 20-25 миль друг от друга.
create table zip_code_distances
(
from_zip_code mediumint not null,
to_zip_code mediumint not null,
distance decimal(6,2) default 0.0,
primary key (from_zip_code, to_zip_code),
key (to_zip_code)
)
engine=innodb;
Только включение почтовых индексов в радиусе 20-25 миль друг от друга уменьшает количество строк, которые необходимо сохранить в таблице расстояний, с максимального значения 1,7 миллиарда (42K ^ 2) - 42K до гораздо более управляемых 4 миллионов или поэтому.
Я загрузил файл данных почтового индекса из Интернета, в котором содержались значения долготы и широты всех официальных почтовых индексов США в формате csv:
"00601","Adjuntas","Adjuntas","Puerto Rico","PR","787","Atlantic", 18.166, -66.7236
"00602","Aguada","Aguada","Puerto Rico","PR","787","Atlantic", 18.383, -67.1866
...
"91210","Glendale","Los Angeles","California","CA","818","Pacific", 34.1419, -118.261
"91214","La Crescenta","Los Angeles","California","CA","818","Pacific", 34.2325, -118.246
"91221","Glendale","Los Angeles","California","CA","818","Pacific", 34.1653, -118.289
...
Я написал быструю и грязную программу на C # для чтения файла и вычисления расстояний между каждым почтовым индексом, но только для выходных почтовых индексов, которые попадают в радиус 25 миль:
sw = new StreamWriter(path);
foreach (ZipCode fromZip in zips){
foreach (ZipCode toZip in zips)
{
if (toZip.ZipArea == fromZip.ZipArea) continue;
double dist = ZipCode.GetDistance(fromZip, toZip);
if (dist > 25) continue;
string s = string.Format("{0}|{1}|{2}", fromZip.ZipArea, toZip.ZipArea, dist);
sw.WriteLine(s);
}
}
Результирующий выходной файл выглядит следующим образом:
from_zip_code|to_zip_code|distance
...
00601|00606|16.7042215574185
00601|00611|9.70353520976393
00601|00612|21.0815707704904
00601|00613|21.1780461311929
00601|00614|20.101431539283
...
91210|90001|11.6815708119899
91210|90002|13.3915723402714
91210|90003|12.371251171873
91210|90004|5.26634939906721
91210|90005|6.56649623829871
...
Затем я просто загрузил бы эти данные о расстоянии в свою таблицу zip_code_distances, используя файл загрузки данных, а затем использовал его для ограничения пространства поиска моего приложения.
Например, если у вас есть пользователь с почтовым индексом 91210, и он хочет найти людей, которые находятся в радиусе 10 миль от них, то теперь вы можете просто сделать следующее:
select
p.*
from
people p
inner join
(
select
to_zip_code
from
zip_code_distances
where
from_zip_code = 91210 and distance <= 10
) search
on p.zip_code = search.to_zip_code
where
p.gender = 'F'....
Надеюсь, это поможет
EDIT: расширен радиус до 100 миль, что увеличило количество почтовых индексов до 32,5 миллионов строк.
быстрая проверка производительности для почтового индекса 91210, время выполнения 0,009 секунды.
select count(*) from zip_code_distances
count(*)
========
32589820
select
to_zip_code
from
zip_code_distances
where
from_zip_code = 91210 and distance <= 10;
0:00:00.009: Query OK