SQL query, выберите ближайшие места по заданным координатам [дубликат]

этот вопрос уже есть ответ здесь:

Я $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;

как вы думаете, это правильно или вы предлагаете что-то другое?

как сделать BEETWEEN, так как я хочу искать через максимум 50 км диапазон ближайших мест?

Я забыл сказать, что я также могу использовать PHP для чего-либо, прежде чем запускать запрос.

Примечание: я не могу использовать хранимые процедуры.

4 ответов


здесь 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";

и вы можете посмотреть здесь для подобных вычислений.

а можно подробнее здесь

обновление:

вы должны принять во внимание, что для расчета longitude2 и longitude2 вы должны знать, что:

каждый градус широта примерно 69 миль (111 км) друг от друга. диапазон варьируется (из-за слегка эллипсоидной формы Земли) от 68,703 миль (110,567 км) на экваторе до 69,407 (111,699 км) на полюсах. Это удобно, потому что каждая минута (1/60 градуса) составляет примерно одну милю.

степень долгота is самый широкий на экваторе при 69.172 миль (111.321) и постепенно уменьшается до нуля на полюсах. При 40° северной или Южной долготы расстояние между градусом долготы составляет 53 мили (85 км).

так вычислить $longitude2 $latitude2 согласно 50км после этого примерно так:

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

вы должны учитывать, что наводнение любой СУБД, как MySQL с тяжелыми запросами не должно быть лучшим решением.

вместо этого вы можете спекулировать очень быстрым SQL-запросом, выбирая все места с координатами внутри простого квадрата стороны $radius, вместо того, чтобы выбрать suddently идеальный радиус круга. PHP может фильтровать излишки.

позвольте мне показать простую концепцию:

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

$angle_radius = $radius / ( 111 * cos( $lat ) ); // Every lat|lon degree° is ~ 111Km
$min_lat = $lat - $angle_radius;
$max_lat = $lat + $angle_radius;
$min_lon = $lon - $angle_radius;
$max_lon = $lon + $angle_radius;

$results = $db->get_results("... WHERE latitude BETWEEN $min_lat AND $max_lat AND longitude BETWEEN $min_lon AND $max_lon"); // A own function that return an array full with all the results rows

$n_rows = count($results);
for($i=0; $i<$n_rows; $i++) {
    if(getDistanceBetweenPointsNew($lat, $lon, $results[$i]->latitude, $results[$i]->longitude, 'Km') > $radius) {
        // This is out of the "perfect" circle radius. Strip it out.
        unset($results[$i]);
    }
}

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

так MySQL выполняет очень дружественный запрос, который не требует прочитайте весь набор баз данных и PHP удаляет излишек с чем-то похожим на getDistanceBetweenPointsNew() функция размещена на этой странице, сравнивая расстояние от координат результирующего набора до центра вашего радиуса.

чтобы не тратить (большой) прирост производительности, индексируйте столбцы координат в базе данных.

всего доброго!


Я сделал что-то подобное с приложением продажи домов, заказывая по расстоянию от заданной точки, поместите это в свой оператор 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 являются столбцами SQL, которые содержат значения lat/lng. Затем вы можете заказать результаты по расстоянию и использовать предложение where или having для фильтрации наших свойств в диапазоне 50 км.

Я бы рекомендовал использовать SQL в качестве подход по сравнению с PHP в случае, если вам требуется дополнительная функциональность, такая как подкачка.


немного поздно, но это может помочь кому - то-если вы хотите ближайший город по местоположению, я бы не пошел на расстояние, потому что тогда изолированное место ничего не получит. Попробуйте это:

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