Мне нужно написать запрос, который позволит мне найти все местоположения в пределах диапазона (миль) из указанного местоположения.
Таблица выглядит следующим образом:
id | name | lat | lng
Итак, я провел исследование и обнаружил: это мое представление sql
Я проверил егона столе около 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;
Это вернуло строки примерно за 50ms , что довольно хорошо!Однако это приведет к значительному замедлению при увеличении количества строк.
EXPLAIN
показывает, что он использует только ПЕРВИЧНЫЙ ключ, что очевидно.
Затем, после прочтения статьи , указанной выше.Я пытался что-то вроде этого:
// 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 = @mylon-@dist/abs(cos(radians(@mylat))*69);
set @lon2 = @mylon+@dist/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;
Время этого запроса составляет около 240 мс , это не так уж плохо, но медленнее, чем в прошлом.Но я могу представить, что при большем количестве строк это будет работать быстрее.Однако EXPLAIN
показывает возможные ключи как lat
, lng
или PRIMARY
и используется PRIMARY
.
Как я могу сделать это лучше ???
Я знаю, что мог бы сохранить лат в качестве ТОЧКИ ();но я также не нашел слишком много документации по этому вопросу, которая показывает, является ли это быстрее или точнее?
Любые другие идеи будут с радостью приняты!
Большое спасибо!
-Stefan
ОБНОВЛЕНИЕ:
Как отметил Джонатан Леффлер, я допустил несколько ошибок, которые не заметил:
Iтолько поместил abs () в одно из значений lat.Я использовал поиск по идентификатору в предложении WHERE и во втором, когда в этом не было необходимости.Первый запрос был чисто экспериментальным, а второй с большей вероятностью попадет в производство.
После этих изменений EXPLAIN
показывает, что ключ теперь использует столбец lng
и среднее время ответа около 180 мс сейчас, что является улучшением.