php/mysql邮政编码邻近搜索
我只是在寻找最好的方法来做这件事的建议 我需要创建一个搜索功能,在邮政编码50英里半径范围内搜索“用户”。我有一个邮政编码表,其中包含所有美国邮政编码及其纬度/经度,但我只是想找出构造和查询数据的最佳方法 我是否应该向users表中添加纬度/经度列,并查询给定邮政编码半径内的所有用户?或者我应该在zip codes表中查询半径范围内的所有zip codes,然后在users表中查询具有结果的所有用户(zip codes)?或在这一点上,我愿意接受任何建议php/mysql邮政编码邻近搜索,php,sql,mysql,Php,Sql,Mysql,我只是在寻找最好的方法来做这件事的建议 我需要创建一个搜索功能,在邮政编码50英里半径范围内搜索“用户”。我有一个邮政编码表,其中包含所有美国邮政编码及其纬度/经度,但我只是想找出构造和查询数据的最佳方法 我是否应该向users表中添加纬度/经度列,并查询给定邮政编码半径内的所有用户?或者我应该在zip codes表中查询半径范围内的所有zip codes,然后在users表中查询具有结果的所有用户(zip codes)?或在这一点上,我愿意接受任何建议 谢谢 查看此处的近距离搜索功能: 如果
谢谢 查看此处的近距离搜索功能:
如果您的数据采用相同的符号/投影/格式(无论称为什么),它可能适合您。每个邮政编码的纬度/长度是该邮政编码的地理中心,对吗?因此,如果您首先找到地理中心在50英里以内的邮政编码,然后找到这些邮政编码中的用户,您很容易返回50英里以外的用户。这样做会牺牲一些准确性 但是如果您有很多用户(超过邮政编码的数量),这会更快,因为您首先要查询较小的邮政编码表。您可以在users表中索引邮政编码,因此查找具有特定邮政编码的用户会很快 只是一些想法!因此,如果你希望有很多用户,而50英里的半径不需要精确,我会在50英里内找到邮政编码,然后在这些邮政编码内找到用户。 我觉得这非常棒 “在“邮政编码”表中查询半径范围内的所有邮政编码,然后在“用户”表中查询具有结果的所有用户(邮政编码)” 我发现这是最好的方法,除非你需要把用户放在谷歌地图上。如果您只是列出里程范围内的用户,那么查询数据库(使用类)以获得ZIP列表应该非常容易,然后选择这些ZIP代码中的所有用户
Select * from Users where zip_code IN (19125,19081,19107.........);
应该可以了。这是我找到的最好的方法。当然,它需要在数据库中对所有zipcodes lat/lon进行编码
// get all the zipcodes within the specified radius - default 20
function zipcodeRadius($lat, $lon, $radius)
{
$radius = $radius ? $radius : 20;
$sql = 'SELECT distinct(ZipCode) FROM zipcode WHERE (3958*3.1415926*sqrt((Latitude-'.$lat.')*(Latitude-'.$lat.') + cos(Latitude/57.29578)*cos('.$lat.'/57.29578)*(Longitude-'.$lon.')*(Longitude-'.$lon.'))/180) <= '.$radius.';';
$result = $this->db->query($sql);
// get each result
$zipcodeList = array();
while($row = $this->db->fetch_array($result))
{
array_push($zipcodeList, $row['ZipCode']);
}
return $zipcodeList;
}
//获取指定半径内的所有zipcodes-默认值为20
函数zipcodeRadius($lat、$lon、$radius)
{
$radius=$radius?$radius:20;
$sql='从ZipCode中选择不同的(ZipCode),其中(3958*3.1415926*sqrt((纬度-'.$lat.)*(纬度-'.$lat.)+cos(纬度/57.29578)*cos('.$lat./57.29578)*(经度-'.$lon.*)*(经度-'.$lon.)/180)我会考虑先用边界平方来减少候选者的数量,然后把半径作为第二步来考虑。从ZIPCODE的坐标开始,然后计算所有4个方向的50英里长/LAT,然后用简单的大/小于标准选择那个框中的候选。如果你的用户基础是很好地展开,这将大大减少候选集,然后您只需进行向量距离数学来消除“角点”。
从这里开始,但请注意解决方案不是很快:
现在,为了加快速度,我们将替换查找以使用空间索引:)
使用MySQL
在数据库中添加一个名为location的列,并使其类型为POINT
确保它现在接受空值
运行以下SQL查询
UPDATE zip_code SET location=PointFromText(CONCAT('POINT('lon','lat'));
现在,使列不接受null
向“位置”列添加空间索引
在上述项目的代码中,将函数“get_zips_In_range”替换为以下内容:
function get_zips_in_range($zip, $range, $sort=1, $include_base)
{
// returns an array of the zip codes within $range of $zip. Returns
// an array with keys as zip codes and values as the distance from
// the zipcode defined in $zip.
$this->chronometer(); // start the clock
$details = $this->get_zip_point($zip); // base zip details
if ($details == false) return false;
// This portion of the routine calculates the minimum and maximum lat and
// long within a given range. This portion of the code was written
// by Jeff Bearer (http://www.jeffbearer.com). This significanly decreases
// the time it takes to execute a query. My demo took 3.2 seconds in
// v1.0.0 and now executes in 0.4 seconds! Greate job Jeff!
// Find Max - Min Lat / Long for Radius and zero point and query
// only zips in that range.
$lat = $details[0];
$lon = $details[1];
$return = array(); // declared here for scope
$first = true;
$radius = $range/69.172;
$boundary = "POLYGON((";
for($i=0; $i <= 360; $i += 360/24)
{
if($first)
{
$first = false;
}
else
{
$boundary .= ', ';
}
$clon = $radius*cos(deg2rad($i)) + $lon;
$clat = $radius*sin(deg2rad($i)) + $lat;
$boundary .= "$clon $clat" ;
}
$boundary .= '))';
$sql = "SELECT zip_code, city, county, state_name, state_prefix, area_code, time_zone, lat, lon FROM zip_code WHERE MBRContains(GeomFromText('$boundary'), location);";
//echo $sql;
$r = mysql_query($sql);
if (!$r) { // sql error
$this->last_error = mysql_error();
return false;
} else {
while ($row = mysql_fetch_row($r)) {
// loop through the results to get the milage from src
$dist = $this->calculate_mileage($details[0],$row[7],$details[1],$row[8]);
if ($this->units == _UNIT_KILOMETERS) $dist = $dist * _M2KM_FACTOR;
$return[str_pad($row[0].', '.$row[1], 5, "0", STR_PAD_LEFT)] = round($dist, $this->decimals);
}
mysql_free_result($r);
}
// sort array
switch($sort)
{
case _ZIPS_SORT_BY_DISTANCE_ASC:
asort($return);
break;
case _ZIPS_SORT_BY_DISTANCE_DESC:
arsort($return);
break;
case _ZIPS_SORT_BY_ZIP_ASC:
ksort($return);
break;
case _ZIPS_SORT_BY_ZIP_DESC:
krsort($return);
break;
}
$this->last_time = $this->chronometer();
if (empty($return)) return false;
return $return;
}
函数get_zips_in_range($zip,$range,$sort=1,$include_base)
{
//返回$zip.returns范围内的邮政编码数组
//一个数组,其中键为邮政编码,值为距离
//在$zip中定义的zipcode。
$this->chronometer();//启动时钟
$details=$this->get_zip_point($zip);//基本zip详细信息
如果($details==false),则返回false;
//例程的这一部分计算最小和最大lat和
//在给定的范围内很长。这部分代码是编写的
//杰夫·贝尔纳(http://www.jeffbearer.com).这大大减少了
//执行查询所需的时间。我的演示耗时3.2秒
//1.0.0版,现在只需0.4秒即可执行!太棒了,杰夫!
//查找半径和零点的最大-最小纬度/长度并查询
//只有拉链在那个范围内。
$lat=$details[0];
$lon=$details[1];
$return=array();//此处为作用域声明
$first=true;
$radius=$range/69.172;
$boundary=“多边形(”;
对于($i=0;$i last_error=mysql_error());
返回false;
}否则{
而($row=mysql\u fetch\u row($r)){
//循环遍历结果以从src获得milage
$dist=$this->calculate_里程($details[0],$row[7],$details[1],$row[8]);
如果($this->units==\u UNIT\u km)$dist=$dist*\u M2KM\u系数;
$return[str_pad($row[0]。,'.$row[1],5,“0”,str_pad_LEFT)]=舍入($dist,$this->小数);
}
mysql_免费_结果($r);
}
//排序数组
开关($sort)
{
案例(拉链)(按距离排序)(ASC):
asort(收益);
打破
案例_ZIPS_SORT_BY_DISTANCE_DESC:
阿索特(返回);
打破
箱子(拉链)(按拉链)分类(ASC):
ksort(返回);
打破
箱子(拉链)(按拉链分类)(说明):
krsort($返回);
打破
}
$this->last_time=$this->chronometer();
if(空($return))返回false;
return$return;
}
我会先
$query = 'SELECT zzip FROM ' . table .
' WHERE (POW((69.1*(zlongitude-"' .
$long . '")*cos(' . $long .
'/57.3)),"2")+POW((69.1*(zlatitude-"' .
$lat . '")),"2"))<(' . $radius .
'*' . $radius . ')';