Мне нужно написать запрос, который позволяет мне найти все местоположения в пределах диапазона (миль) из предоставленного местоположения.Расстояние между двумя координатами, как я могу упростить это и/или использовать другую технику?
таблица выглядит так:
id | name | lat | lng
Так я делал исследование и обнаружили: this my sql presentation
Я испытал его на стол с около 100 строк и будет иметь много больше! - Должен быть масштабируемым.
Я пытался что-то более простое, как это первая:
//just some test data this would be required by user input
set @orig_lat=55.857807; set @orig_lng=-4.242511; set @dist=10;
SELECT *, 3956 * 2 * ASIN(
SQRT(POWER(SIN((orig.lat - abs(dest.lat)) * pi()/180/2), 2)
+ COS(orig.lat * pi()/180) * COS(abs(dest.lat) * pi()/180)
* POWER(SIN((orig.lng - dest.lng) * pi()/180/2), 2)))
AS distance
FROM locations dest, locations orig
WHERE orig.id = '1'
HAVING distance < 1
ORDER BY distance;
Это возвращенное строки в вокруг 50мс который очень хорошо! Однако это резко снизится по мере увеличения рядов.
EXPLAIN
показывает, что используется только ключ PRIMARY.
Затем после прочтения статьи linked above. Я пытался что-то вроде этого:
// defining variables - this when made into a stored procedure will call
// the values with a SELECT query.
set @mylon = -4.242511;
set @mylat = 55.857807;
set @dist = 0.5;
-- calculate lon and lat for the rectangle:
set @lon1 = @[email protected]/abs(cos(radians(@mylat))*69);
set @lon2 = @[email protected]/abs(cos(radians(@mylat))*69);
set @lat1 = @mylat-(@dist/69);
set @lat2 = @mylat+(@dist/69);
-- run the query:
SELECT *, 3956 * 2 * ASIN(
SQRT(POWER(SIN((@mylat - abs(dest.lat)) * pi()/180/2) ,2)
+ COS(@mylat * pi()/180) * COS(abs(dest.lat) * pi()/180)
* POWER(SIN((@mylon - dest.lng) * pi()/180/2), 2)))
AS distance
FROM locations dest
WHERE dest.lng BETWEEN @lon1 AND @lon2
AND dest.lat BETWEEN @lat1 AND @lat2
HAVING distance < @dist
ORDER BY distance;
Время этого запроса составляет около 240ms, это не так уж плохо, но медленнее, чем в прошлом. Но я могу себе представить, что при гораздо более высоком числе строк это будет работать быстрее. Однако EXPLAIN
показывает возможные ключи как lat
, lng
или PRIMARY
и используется PRIMARY
.
Как я могу сделать это лучше ???
Я знаю, что могу хранить lat lng как POINT(); но я также не нашел слишком много документации по этому поводу, которая показывает, является ли она более быстрой или точной?
Любые другие идеи были бы с радостью приняты!
Большое спасибо!
-Stefan
UPDATE:
Как Джонатан Леффлера отметил, что я сделал несколько ошибок, которые я не заметил:
я только положить абс() на одном из значений lat. Я использовал поиск id в предложении WHERE во втором, когда не было необходимости. В первом запросе была чисто экспериментальная, вторая, скорее всего, поразила производство.
После этих изменений EXPLAIN
показывает ключ теперь использует lng
колонки и среднее время, чтобы ответить вокруг Теперь звука 180 мс, который является усовершенствованием.
Стефан, я ищу, чтобы сделать что-то подобное .. можете ли вы опубликовать свою окончательную хранимую процедуру? Я никогда не писал хранимую процедуру раньше, первый вопрос, который приходит на ум, - ваш код выглядит так, как будто он имеет статические параметры .. как я могу передать myLat, myLon и расстояние до хранимой процедуры, и это расстояние «мили» « – erik