Подтвердить что ты не робот

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;

Считаете ли вы это правильным или вы предлагаете что-то еще?

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

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

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

4b9b3361

Ответ 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";

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

и вы можете читать здесь

Update:

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

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

Степень долготы самая широкая на экваторе при 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

Ответ 2

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

Вместо этого вы можете спекулировать очень быстрым SQL-запросом, выбирая все места с координатами внутри простого квадрата бокового $radius, вместо того, чтобы случайно выбирать идеальный радиус окружности. 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() размещенную на этой странице, сравнивая расстояние от координат набора результатов до центра. вашего радиуса.

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

Удачного взлома!

Ответ 3

Я сделал что-то похожее с приложением для продажи домов, заказав расстояние от заданной точки, поместите это в свой оператор select SQL:

((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, если вам нужны дополнительные функции, такие как пейджинг.

Ответ 4

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

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