SQL查询,通过给定坐标选择最近的地方

2022-08-30 23:01:54

我有和,现在在MySQL中,我想把15个最近的地方带到这些坐标,我打算做这个查询:$latitude = 29.6815400$longitude = 64.3647100

SELECT *
FROM places
WHERE latitude  BETWEEN($latitude  - 1, $latitude  + 1)
AND   longitude BETWEEN($longitude - 1, $logintude + 1)
LIMIT 15;

你认为这是正确的还是你提出了其他建议?

如何做,因为我想搜索槽最大50Km范围的附近地方?BEETWEEN

我忘了说,在运行查询之前,我也可以使用PHP做任何事情。

注意:我不能使用存储过程


答案 1

这是用于计算两点之间距离的PHP公式:

function getDistanceBetweenPointsNew($latitude1, $longitude1, $latitude2, $longitude2, $unit = 'Mi') 
{
   $theta = $longitude1 - $longitude2;
   $distance = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))+
               (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)));
   $distance = acos($distance); $distance = rad2deg($distance); 
   $distance = $distance * 60 * 1.1515;

   switch($unit) 
   { 
     case 'Mi': break;
     case 'Km' : $distance = $distance * 1.609344; 
   } 
   return (round($distance,2)); 
}

然后添加一个查询以获取距离小于或等于上述所有记录:

$qry = "SELECT * 
        FROM (SELECT *, (((acos(sin((".$latitude."*pi()/180)) *
        sin((`geo_latitude`*pi()/180))+cos((".$latitude."*pi()/180)) *
        cos((`geo_latitude`*pi()/180)) * cos(((".$longitude."-
        `geo_longitude`)*pi()/180))))*180/pi())*60*1.1515*1.609344) 
        as distance
        FROM `ci_geo`)myTable 
        WHERE distance <= ".$distance." 
        LIMIT 15";

你可以在这里看看类似的计算。

你可以在这里阅读更多

更新:

你必须记住,要计算经度2经度2,你需要知道:

每个纬度距约69英里(111公里)。范围从赤道的68.703英里(110.567公里)到两极的69.407英里(111.699公里)不等(由于地球的略微椭圆体形状)。这很方便,因为每分钟(1/60度)大约是一英里。

经度在赤道最宽,为69.172英里(111.321),并在两极逐渐缩小到零。在北纬或南纬 40° 处,经度之间的距离为 53 英里(85 公里)。

所以根据50公里计算,然后大约:$longitude2 $latitude2

$longitude2 = $longitude1 + 0.449; //0.449 = 50km/111.321km
$latitude2 = $latitude1 + 0.450; // 0.450 = 50km/111km

答案 2

根据经验,始终尽量不要用繁重的查询淹没MySQL。

相反,您可以推测一个非常快速且非常优化的SQL查询,选择坐标在一个简单的边的正方形内的所有位置,而不是突然选择具有该半径的完美圆。然后,或者任何可以快速过滤剩余的东西。$radiusPHP

让我用PHP来展示这个概念:

// your input variables
$lat    = 45.6072;
$lon    = 7.65678;
$radius = 50; // expressed in Km

// every latitude degree° is ~ 111Km
$angle_radius_lat = $radius / 111;

// longitude takes into account equator distance
$angle_radius_lon = $angle_radius_lat * cos( deg2rad( $lat ) );

// define a simple square with your lat/lng as center
$min_lat = $lat - $angle_radius_lat;
$max_lat = $lat + $angle_radius_lat;
$min_lon = $lon - $angle_radius_lon;
$max_lon = $lon + $angle_radius_lon;

// query places inside the square
// use here your own database function
$results_raw = query_results( "SELECT latitude, longitude FROM ... WHERE latitude BETWEEN $min_lat AND $max_lat AND longitude BETWEEN $min_lon AND $max_lon" );

// filter the surplus outside the circle
$results = [];
foreach( $results_raw as $result ) {

    if( getDistanceBetweenPointsNew( $lat, $lon, $result->latitude, $result->longitude, 'Km' ) <= $radius ) {
        $results[] = $result;
    }

}

// these are your places inside the circle of your radius
var_dump( $results );

通过这种方式,MySQL运行一个非常友好的查询,不会导致完整的表扫描,而PHP则使用类似于此页面中发布的函数的内容去除多余的查询,比较从结果集的坐标到半径中心的距离。getDistanceBetweenPointsNew()

重要提示:为了不浪费巨大的性能提升,请在坐标列( 和 ) 上创建一个数据库索引。祝您黑客入侵愉快!latitudelongitude


推荐