Как лучше всего осуществить поиск ближайшего соседа в MySQL?


10

Короче говоря,

  1. Каким должен быть тип данных широты и долготы?
  2. Какую команду SQL я должен вызвать, чтобы получить первые 100 ближайших ресторанов, например?

Деталь:

У меня есть 100-килограммовая запись с широтой и долготой. Я вижу, что MySQL на самом деле поддерживает тип данных с именем point. Должен ли я использовать это вместо этого?

Поддерживает ли MySQL систему хранения KDTree http://en.wikipedia.org/wiki/File:KDTree-animation.gif

Лучше ли использовать точечный тип данных, а не обычный тип данных с плавающей точкой для хранения широты и долготы?

В конце концов я хочу найти такие вещи, как, например, первые 100 ресторанов, наиболее близкие к пунктам 105,6, и в моей базе данных много бизнесов и пунктов. Очевидно, что вычисление расстояния один за другим для каждой записи и для каждой точки будет O (n) и, следовательно, отстой.

Обратите внимание, что мне известно о более простом решении, описанном в разделе «Как сделать приложение вроде Yelp». Эффективно извлекайте информацию о расстоянии из базы данных и внедряйте ее самостоятельно для начала. Это хороший ответ.

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

Где я могу узнать больше об этом? Спасибо.



Похоже, что решение здесь dba.stackexchange.com/questions/4210/… является лучшим решением. Я имею в виду, что есть такая вещь, которая называется MYSQL пространственная. Однако вы не можете вытащить такие вещи, как где (расстояние (х) <20). Это еще не реализовано.
user4951

Ответы:


11

Что касается шаблонов проектирования, вопрос о Yelp - довольно стандартный материал.

Для более сложного ответа вам, вероятно, понадобится геопространственное расстояние. Вот это увлекательная PowerPoint об этой теме (и здесь есть версия ПДФ , что хорошо). Тем не менее, математика довольно некрасивая.

С их слайда:

set @orig_lat=122.4058; set @orig_lon=37.7907;
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_lon  dest.lon) * pi()/180 / 2), 2) )) as  distance
FROM hotels dest 
having distance < @dist
ORDER BY distance limit 10

Более длинный, более подробный ответ о геопространственном расстоянии в переполнении стека .

Но вы все еще хотите ограничить результаты широтой и долготой.

В конечном счете, я бы использовал тип данных POINT и использовал бы широту / долготу. В настоящее время нет способа определить расстояние между двумя точками, поэтому вам все равно придется хранить широту / долготу для этого расчета.

Одна последняя ссылка: вы также можете проверить этот поток SO относительно ускорения запросов с использованием пространственных индексов.


[ОШИБКА в запросе 4] В вашем синтаксисе SQL есть ошибка; проверьте руководство, соответствующее вашей версии сервера MySQL, чтобы узнать правильный синтаксис для использования рядом с '- dest.lon) * pi () / 180/2), 2))) как расстояние от network_pos dest с d' в строке 2
Фелипе

Привет, @ Dist на Миллес? спасибо
Хорхе Олаф Эрландсен

1
@OlafErlandsen да, это в миле
Ян ван дер Вегт

4

Точечные типы данных в порядке; Вы можете просто вызвать X (координаты) / Y (координаты), чтобы получить значения широты / долготы.

Например:

SELECT id, 
(3959 
    * acos(
        cos(radians(37)) 
        * cos(radians(Y(coord)))
        * cos(radians(X(coord)) - radians(-122)) 
        + sin(radians(37))
        * sin(radians(Y(coord)))
      )
) AS distance 
FROM markers HAVING distance < 25 
ORDER BY distance LIMIT 20;

37 это лат, а -122 это долг? А 25 это метров или км?
Фелипе

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.