расчеты расстояния в запросах mysql - PullRequest
1 голос
/ 21 октября 2010

Мне нужно запросить базу данных из тысяч записей и упорядочить ее по расстоянию от указанной точки.

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

Есть ли способ встроить это в запрос mysql, так что мне нужно получить только ближайшие 15 записей.

Например

`SELECT events.id, caclDistance($latlng, events.location) AS distance FROM events ORDER BY distance LIMIT 0,15`

    function caclDistance($old, $new){
       //Calculates the distance between $old and $new
    }

Ответы [ 5 ]

9 голосов
/ 21 октября 2010

Вариант 1: Выполните расчет в базе данных, переключившись на базу данных, которая поддерживает GeoIP.

Вариант 2: Выполните вычисления в базе данных, используя хранимую процедуру, например:

CREATE FUNCTION calcDistance (latA double, lonA double, latB double, LonB double)
    RETURNS double DETERMINISTIC
BEGIN
    SET @RlatA = radians(latA);
    SET @RlonA = radians(lonA);
    SET @RlatB = radians(latB);
    SET @RlonB = radians(LonB);
    SET @deltaLat = @RlatA - @RlatB;
    SET @deltaLon = @RlonA - @RlonB;
    SET @d = SIN(@deltaLat/2) * SIN(@deltaLat/2) +
    COS(@RlatA) * COS(@RlatB) * SIN(@deltaLon/2)*SIN(@deltaLon/2);
    RETURN 2 * ASIN(SQRT(@d)) * 6371.01;
END//

Если в вашей базе данных есть индекс широты и долготы, вы можете уменьшить количество вычислений, которые необходимо рассчитать, разработав начальный ограничивающий прямоугольник в PHP ($ minLat, $ maxLat, $ minLong и $ maxLong) и ограничение строк подмножеством ваших записей на основе этого (ГДЕ широта между $ minLat и $ maxLat И долгота между $ minLong И $ maxLong). Тогда MySQL нужно только выполнить расчет расстояния для этого подмножества строк.

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

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

Если вы считаете, что в основном вы рисуете круг на карте с центром в начальной точке и с радиусом расстояния; тогда формула просто определяет, какие строки попадают в этот круг ... но она все равно должна проверять каждую строку.

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

В вашем PHP (предположим, вы используете PHP из имени переменной $), мы можем использовать очень простой расчет, который определяет минимальную и максимальную широту и долготу на основе нашего расстояния, а затем установить эти значения в предложении WHERE. вашего оператора SQL. Это фактически наша коробка, и все, что выпадает за пределы этого, автоматически отбрасывается без необходимости фактически вычислять его расстояние.

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

EDIT Значение 6371.01 в хранимой процедуре calcDistance является множителем, который возвращает результат в километрах. Используйте соответствующие альтернативные множители, если хотите, чтобы в милях, морских милях, метрах и т.д.

5 голосов
/ 21 октября 2010
SELECT events.id FROM events 
ORDER BY pow((lat - pointlat),2) + pow((lon - pointlon),2) ASC
 LIMIT 0,15

Вам не нужно рассчитывать абсолютное расстояние в метрах, используя радиус Земли и т. Д.

Чтобы получить самые близкие точки, вам нужны только точки, упорядоченные с относительным расстоянием.

1 голос
/ 21 октября 2010
0 голосов
/ 21 октября 2010

Если ваш вопрос относится к типу вопроса "найди мой ближайший" или "поиск магазина", вы можете воспользоваться Google для этих условий.Однако, как правило, этот тип данных сопровождается почтовым индексом некоторого описания, и можно сузить список (как отмечает Марк Мейкер) по ассоциации с почтовым кодом.

Каждый случай индивидуален,и это может не относиться к вам, просто выбросить его туда.

0 голосов
/ 21 октября 2010

я думаю хранимые процедуры - это то, что вы ищете.

...