web-dev-qa-db-ja.com

SQLクエリ、指定された座標で最も近い場所を選択

私が持っています $latitude = 29.6815400および$longitude = 64.3647100、MySQLでは、これらの座標に最も近い15の場所を取得したいと考えています。このクエリを実行する予定です。

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

あなたはそれが正しいと思いますか、それとも何か他のものを提案しますか?

近くの場所で最大50Kmの範囲で谷を検索したいので、BEETWEENを実行するにはどうすればよいですか?

クエリを実行する前にPHPを使用して何かを実行することもできます。

注:ストアドプロシージャは使用できません

15
itsme

[〜#〜] php [〜#〜]の計算式は2点間の距離を計算するためのものです:

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";

同様の計算については、 ここ をご覧ください。

そしてあなたはもっと読むことができます ここ

更新:

longitude2およびlongitude2を計算するには、次のことを知っておく必要があることを覚えておく必要があります。

latitudeの各度合いは、およそ69マイル(111キロメートル)離れています。範囲は、赤道で68.703マイル(110.567 km)から変化します(地球のわずかに楕円形のため)。極で69.407(111.699キロ)に。毎分(1/60度)は約1マイルなので、これは便利です。

経度の程度は、赤道の69.172マイル(111.321)で最も広く、極で次第に減少してゼロになります。北または南に40°の場合、経度間の距離は53マイル(85 km)です。

したがって、50 kmに応じて$longitude2 $latitude2を計算するにはおよそ:

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

MySQLのようなDBMSに大量のクエリをあふれさせることが最善の解決策ではないことを考慮する必要があります。

代わりに、突然完全な円の半径を選択する代わりに、辺$radiusの単純な正方形の内側の座標を持つすべての場所を選択する非常に高速なSQLクエリを推測できます。 PHPは、余剰をフィルタリングできます。

コンセプトをお見せしましょう:

$lat    = 45.0.6072;
$lon    = 7.65678;
$radius = 50; // Km

 // Every lat|lon degree° is ~ 111Km
$angle_radius = $radius / ( 111 * cos( $lat ) );

$min_lat = $lat - $angle_radius;
$max_lat = $lat + $angle_radius;
$min_lon = $lon - $angle_radius;
$max_lon = $lon + $angle_radius;

$results = $db->getResults("... WHERE latitude BETWEEN $min_lat AND $max_lat AND longitude BETWEEN $min_lon AND $max_lon"); // your own function that return your results (please sanitize your variables)

$filtereds = [];
foreach( $results as $result ) {
    if( getDistanceBetweenPointsNew( $lat, $lon, $result->latitude, $result->longitude, 'Km' ) <= $radius ) {
        // This is in "perfect" circle radius. Strip it out.
        $filtereds[] = $result;
    }
}

// Now do something with your result set
var_dump( $filtereds );

このように、MySQLはPHPがこのページに投稿されたgetDistanceBetweenPointsNew()関数に類似したもので余剰を取り除き、半径の中心への結果セットの座標。

(大きな)パフォーマンスの向上を無駄にしないために、データベースの座標列にインデックスを付けます。

ハッキングハッピー!

15
Valerio Bozz

私は売り所のアプリで同じようなことをしました。与えられたポイントからの距離で並べ替え、これをSQLのselectステートメントに配置します。

((ACOS(SIN(' . **$search_location['lat']** . ' * PI() / 180) * SIN(**map_lat** * PI() / 180) + COS(' . **$search_location['lat']** . ' * PI() / 180) * COS(**map_lat** * PI() / 180) * COS((' . **$search_location['lng']** . ' - **map_lng**) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS "distance"

置換$search_location関連するlat/lng値とmap_lat/map_lng値は、lat/lng値を含むSQL列です。次に、距離順に結果を並べ替え、whereまたはhaving句を使用して、50 kmの範囲内でプロパティをフィルタリングできます。

PHPと比較して、SQLをアプローチとして使用することをお勧めします。ページングなどの追加機能が必要な場合に使用します。

2
Timmytjc

少し遅れますが、それは誰かを助けるかもしれません-場所で最も近い都市が必要な場合、孤立した場所は何も取得しないので、私は距離に行きません。これを試して:

$G_how_close_to_find_cities = "1.1"; // e.g. 1.1 = 10% , 1.2=20% etc
$G_how_many_cities_to_find_by_coordinates = "10";
$query = "SELECT * from Cities  WHERE 
                        Cities__Latitude <= ('".$latitude*$G_how_close_to_find_cities."') AND Cities__Latitude >= ('".$latitude/$G_how_close_to_find_cities."') 
                    AND Cities__Longitude <= ('".$longitude*$G_how_close_to_find_cities."') AND Cities__Longitude >= ('".$longitude/$G_how_close_to_find_cities."') 
                    ORDER BY SQRT(POWER((Cities__Latitude - ".$latitude."),2)+POWER((Cities__Longitude - ".$longitude."),2)) LIMIT 0,".$G_how_many_cities_to_find_by_coordinates;
0
PalDev