В настоящее время у меня есть чуть менее миллиона мест в базе данных MySQL с информацией о долготе и широте.
Я пытаюсь найти расстояние между одной точкой и многими другими точками с помощью запроса. Это не так быстро, как я хочу, особенно с 100+ ударами в секунду.
Есть ли более быстрый запрос или, возможно, более быстрая система, чем MySQL для этого? Я использую этот запрос:
SELECT
name,
( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) )
* cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763))
* sin( radians(locations.lat)))) AS distance
FROM locations
WHERE active = 1
HAVING distance < 10
ORDER BY distance;
Примечание. Указанное расстояние указывается в милях . Если вам нужны километры , используйте 6371
вместо 3959
.
Ответы:
Создайте свои очки, используя
Point
значенияGeometry
типов данных вMyISAM
таблице. Начиная с Mysql 5.7.5,InnoDB
таблицы теперь также поддерживаютSPATIAL
индексы.Создать
SPATIAL
индекс по этим точкамИспользуйте,
MBRContains()
чтобы найти значения:или, внутри
MySQL 5.1
и выше:Это выберет все точки примерно в пределах окна
(@lat +/- 10 km, @lon +/- 10km)
.На самом деле это не прямоугольник, а сферический прямоугольник: связанный с широтой и долготой сегмент сферы. Это может отличаться от простого прямоугольника на Земле Франца-Иосифа , но довольно близко к нему в большинстве населенных мест.
Примените дополнительную фильтрацию, чтобы выделить все внутри круга (не квадрата)
Возможно применение дополнительной тонкой фильтрации для учета большого круга (для больших расстояний)
источник
@lon - 10 / ( 111.1 / cos(@lat))
(и будет второй в паре, когда все будет правильно.cos(lon)
является точным только для небольших расстояний. См. Janmatuschek.de/LatitudeLongitudeBoundingCoordinates111.(1)
км в градусе широты.mypoint
поле в таблице, в котором хранятся координатыНе специфичный для MySql ответ, но он улучшит производительность вашего оператора SQL.
То, что вы эффективно делаете, это вычисление расстояния до каждой точки в таблице, чтобы увидеть, находится ли оно в пределах 10 единиц от данной точки.
То, что вы можете сделать перед запуском этого sql, это создать четыре точки, которые нарисуют прямоугольник на 20 единиц на стороне, с вашей точкой в центре, т.е. (х1, у1). , , (x4, y4), где (x1, y1) - это (дано + 10 единиц, дано +10 единиц). , , (дано долго - 10 единиц, дано - 10 единиц). На самом деле, вам нужны только две точки, верхний левый и нижний правый вызов их (X1, Y1) и (X2, Y2)
Теперь ваш оператор SQL использует эти точки, чтобы исключить строки, которые определенно находятся на расстоянии более 10u от вашей заданной точки, он может использовать индексы по широтам и долготам, поэтому будет на несколько порядков быстрее, чем у вас в настоящее время.
например
Боксовый подход может возвращать ложные срабатывания (вы можете подобрать точки в углах поля, которые находятся на расстоянии> 10u от заданной точки), поэтому вам все равно нужно рассчитать расстояние до каждой точки. Однако это снова будет намного быстрее, потому что вы резко ограничили количество проверяемых точек до точек внутри блока.
Я называю эту технику «Мышление внутри коробки» :)
РЕДАКТИРОВАТЬ: это можно поместить в один оператор SQL?
Я понятия не имею, на что способны mySql или Php, извините. Я не знаю, где лучше всего построить четыре точки или как их можно передать в запрос mySql в Php. Однако, когда у вас есть четыре пункта, ничто не помешает вам объединить свой собственный оператор SQL с моим.
Я знаю, что с помощью MS SQL я могу построить оператор SQL, который объявляет четыре числа с плавающей запятой (X1, Y1, X2, Y2) и вычисляет их перед «основным» оператором выбора, как я уже сказал, я понятия не имею, можно ли это сделать с помощью MySql. Однако я все же был бы склонен построить четыре точки в C # и передать их в качестве параметров в SQL-запрос.
Извините, я не могу помочь, если кто-то может ответить на определенные части MySQL и Php этого, не стесняйтесь редактировать этот ответ, чтобы сделать это.
источник
Следующая функция MySQL была опубликована в этом посте . Я не очень проверял это, но из того, что я собрал из поста, если ваши поля широты и долготы проиндексированы , это может хорошо работать для вас:
Пример использования:
Предположим, что таблица вызывается
places
с полямиlatitude
&longitude
:источник
SELECT ROUND(((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lnt1 - lnt2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) * 1.609344 * 1000) AS distance
Мне нужно было решить аналогичную проблему (фильтрация строк по расстоянию от одной точки), и, комбинируя оригинальный вопрос с ответами и комментариями, я нашел решение, которое идеально подходит для меня как на MySQL 5.6, так и на 5.7.
coordinates
Поле с типомPOINT
и имеетSPATIAL
индекс6371
для расчета расстояния в километрах.56.946285
Широта для центральной точки.24.105078
Долгота для центральной точки.15
Максимальное расстояние в километрах.В моих тестах MySQL использует SPATIAL index для
coordinates
поля, чтобы быстро выбрать все строки, которые находятся внутри прямоугольника, а затем вычисляет фактическое расстояние для всех отфильтрованных мест, чтобы исключить места из углов прямоугольников и оставить только места внутри круга.Это визуализация моего результата:
Серые звезды визуализируют все точки на карте, желтые звезды возвращаются по запросу MySQL. Серые звезды внутри углов прямоугольника (но за пределами круга) были выбраны
MBRContains()
и затем отмененыHAVING
.источник
если вы используете MySQL 5.7. *, то вы можете использовать st_distance_sphere (POINT, POINT) .
источник
Это запрос вычисления расстояния между точками в MySQL, я использовал его в длинной базе данных, он работает отлично! Примечание: внесите изменения (имя базы данных, имя таблицы, столбец и т. Д.) В соответствии с вашими требованиями.
источник
источник
источник
источник
Функция MySQL, которая возвращает количество метров между двумя координатами:
Чтобы вернуть значение в другом формате, замените
6371000
функцию в радиусе Земли в выбранной вами единице измерения. Например, километры будут,6371
а мили будут3959
.Чтобы использовать функцию, просто вызовите ее, как любую другую функцию в MySQL. Например, если у вас есть таблица
city
, вы можете найти расстояние между каждым городом и любым другим городом:источник
Полный код с подробной информацией о том, как установить плагин MySQL, находится здесь: https://github.com/lucasepe/lib_mysqludf_haversine
Я опубликовал это в прошлом году как комментарий. Так как любезно @TylerCollier предложил мне опубликовать как ответ, вот оно.
Другой способ - написать пользовательскую функцию UDF, которая возвращает расстояние боярышника от двух точек. Эта функция может принимать на входе:
Таким образом, мы можем написать что-то вроде этого:
получить все записи с расстояния менее 40 километров. Или:
получить все записи с расстояния менее 25 футов.
Основная функция:
источник
Быстрое, простое и точное (для меньших расстояний) приближение может быть выполнено с помощью сферической проекции . По крайней мере, в моем алгоритме маршрутизации я получаю повышение на 20% по сравнению с правильным расчетом. В коде Java это выглядит так:
Не уверен насчет MySQL (извините!).
Убедитесь, что вы знаете об ограничении (третий параметр assertEquals означает точность в километрах):
источник
Вот очень подробное описание Geo Distance Search с MySQL решение, основанное на реализации формулы Haversine для MySQL. Полное описание решения с теорией, реализацией и дальнейшей оптимизацией производительности. Хотя часть пространственной оптимизации не работала правильно в моем случае. http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
источник
Прочитайте Geo Distance Search с MySQL , решение, основанное на реализации формулы Haversine для MySQL. Это полное описание решения с теорией, реализацией и дальнейшей оптимизацией производительности. Хотя часть пространственной оптимизации не работала правильно в моем случае.
Я заметил две ошибки в этом:
использование
abs
в операторе выбора на стр. 8. Я просто опустил,abs
и это сработало.функция расстояния пространственного поиска на p27 не преобразуется в радианы и не умножает долготу на
cos(latitude)
, если только его пространственные данные не загружены с учетом этого (не может сказать из контекста статьи), но его пример на p26 указывает, что его пространственные данныеPOINT
не загружены радианы или градусы.источник
источник
Использование mysql
Смотрите: https://andrew.hedges.name/experiment/haversine/
Смотрите: https://stackoverflow.com/a/24372831/5155484
Смотрите: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
ПРИМЕЧАНИЕ:
LEAST
используется, чтобы избежать нулевых значений в качестве комментария, предложенного на https://stackoverflow.com/a/24372831/5155484источник