php / mysql邮政编码邻近搜索

时间:2010-03-09 16:16:23

标签: php sql mysql

我只是在寻找有关最佳方法的建议......

我需要创建一个搜索功能,搜索邮政编码半径50英里范围内的“用户”。我有一个邮政编码表,其中包含所有美国邮政编码及其纬度/经度,但我只是想找出构建和查询数据的最佳方法......

我应该将纬度/经度列添加到users表并查询给定邮政编码半径范围内的所有用户吗?或者我应该在邮政编码表中查询半径范围内的所有邮政编码,然后查询具有结果的所有用户的用户表(邮政编码)?要么... ???我对此提出任何建议持开放态度!

谢谢!

7 个答案:

答案 0 :(得分:19)

这是我找到的最佳方式。当然,这需要您在数据库中对所有的zip /编码进行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;
}

你应该能够放弃这个功能。将它传递给你想要半径的邮政编码的$ lat和$ lon,包括可选的半径,然后获取一个zipcodes列表。

你可以非常轻松地修改它以使所有用户获得zipcode IN(radius_sql)并让你的列表用户回来。

快乐的编码!

答案 1 :(得分:3)

http://www.micahcarrick.com/04-19-2005/php-zip-code-range-and-distance-calculation.html

我发现这非常棒。

“在邮政编码表中查询半径范围内的所有邮政编码,然后查询具有结果的所有用户的用户表格(邮政编码)”

我发现这是最好的方法,除非您需要将用户放在谷歌地图上。如果您只是列出里程范围内的用户,那么查询数据库(使用该类)以获取拉链列表应该非常容易,然后选择这些邮政编码中的所有用户。

Select * from Users where zip_code IN (19125,19081,19107.........);

应该这样做。

答案 2 :(得分:2)

查看此处的邻近搜索:

Using PHP/MySQL with Google Maps

如果您的数据采用相同的符号/投影/格式(无论如何调用),它可能适合您。

答案 3 :(得分:2)

从这里开始,但请注意解决方案不是很快:

PHP Zip Code Range and Distance Calculation

现在要快速 - 我们将替换查找以使用空间索引:)

  1. 使用MySQL

  2. 在名为location的数据库中添加一列,并将其命名为POINT

  3. 确保它现在接受空值

  4. 运行以下SQL查询

    UPDATE zip_code SET location = PointFromText(CONCAT('POINT(',lon,' ',lat,')'));

  5. 现在,让列不接受空值

  6. 将空间索引添加到位置列

  7. 在上述项目的代码中,使用以下内容替换函数'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;
       }
    

答案 4 :(得分:1)

我会考虑首先减少具有边界的候选者的数量,然后担心第二步的半径。首先使用zipcode的坐标,然后在所有4个方向上计算50英里的长/纬度,然后使用简单的大于/小于标准选择该框中的候选者。如果你的用户群分布很好,这会大大减少你的候选集,那么你只需要做矢量距离数学来消除“角落”。

答案 5 :(得分:1)

我首先要搜索目标半径内的所有zipcodes。然后将所有返回的zipcodes与您的用户表zipcodes进行比较。拉出匹配的用户。

它找到半径的zipcodes,发现这个MySQL调用:

$query = 'SELECT zzip FROM ' . table . 
            ' WHERE (POW((69.1*(zlongitude-"' . 
            $long . '")*cos(' . $long . 
            '/57.3)),"2")+POW((69.1*(zlatitude-"' . 
            $lat . '")),"2"))<(' . $radius . 
            '*' . $radius . ')';

MySQL为你做了所有的数学运算。

我在这里找到了一个使用它的类: http://www.nucleusdevelopment.com/code/do/zipcode

希望有所帮助。

答案 6 :(得分:0)

每个邮政编码的纬度/长度是该邮政编码的地理中心,对吗?因此,如果您首先找到地理中心在50英里范围内的邮政编码,那么使用这些邮政编码的用户,您可以轻松地返回50英里以外的用户。因此,你会牺牲一些准确性。

但是如果你有很多用户(超过邮政编码的数量),这会更快,因为你先查询较小的邮政编码表。您可以在users表中索引邮政编码,因此查找具有特定邮政编码的用户会很快。

只是一些想法!所以,如果你期待很多用户并且50英里的半径不需要精确,我会在50英里内找到邮政编码,然后是那些邮政编码内的用户。