我只需创建一个 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 英里半径内的邮政编码可将距离表中需要存储的行数从最大值 17 亿 (42K ^ 2) - 42K 减少到更易于管理的 400 万左右。
我从网上下载了一个邮政编码数据文件,其中包含 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
...
然后,我将使用 load data infile 将此距离数据加载到我的 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'....
希望这可以帮助
编辑:将半径扩展到 100 英里,这将邮政编码距离的数量增加到 3250 万行。
邮政编码 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