Геопространственный поиск MySQL с использованием формулы гаверсинуса возвращает ноль в той же точке

Я пытаюсь реализовать геопространственный поиск в приложении php. В настоящее время я использую следующий запрос для поиска точек в пределах 10 км от заданной широты и долготы:

SELECT * FROM (
      SELECT *,
             (6378.1 * ACOS(
                  COS(RADIANS(48.856614)) * COS(RADIANS(latitude))
                * COS(RADIANS(2.3522219) - RADIANS(longitude))
                + SIN(RADIANS(48.856614))
                * SIN(RADIANS(latitude)))
             ) AS distance
      FROM `destinations_info`
      WHERE latitude BETWEEN 48.856614  - (10 / 69)
               AND 48.856614  + (10 / 69)
        AND longitude BETWEEN 2.3522219 - (10 / (69 * COS(RADIANS(48.856614))))
               AND 2.3522219 + (10 / (69 * COS(RADIANS(48.856614))))
      ) d
WHERE distance <= 10
LIMIT 1

Это работает нормально, пока я не ищу точную широту и долготу, которые хранятся в таблице POI.

Так, например, если у меня есть в моей таблице poi следующая запись

id     name     latitude    longitude
1      Paris    48.856614   2.3522219000000177

И я вызываю запрос с lat = 48.856614 и long = 2.3522219000000177, я не получу никаких результатов. Насколько я вижу, это происходит потому, что следующая операция возвращает NULL, а не 0 в mysql:

SELECT (6378.1 * acos(cos(radians(48.856614)) * cos(radians(48.856614)) * cos( radians(2.3522219) - radians(2.3522219)) + sin(radians(48.856614)) * sin(radians(48.856614)))) 

Выполнение того же в калькуляторе Chrome Я получаю 0 рад.

Что-то не так в запросе или мне нужно также включить результаты NULL, используя "OR IS NULL"


person Tudor Ravoiu    schedule 31.01.2015    source источник


Ответы (2)


IFNULL( ... ),0) AS Distance также поможет вам обойти эту ошибку.

person Frédéric Peters    schedule 01.04.2016
comment
+1 - у меня возникла эта проблема, когда широта/долгота, указанные в запросе, ТОЧНО совпадают с широтой/долготой записи в базе данных, которую я хочу получить. Хорошее, элегантное решение. - person phirschybar; 02.10.2018

Проблема в том, что acos вызывается с значение, которое не находится в диапазоне от -1 до 1, поэтому возвращает ноль.

SELECT cos(radians(48.856614)) * cos(radians(48.856614)) * cos( radians(2.3522219) - radians(2.3522219)) + sin(radians(48.856614)) * sin(radians(48.856614))

1.0000000000000002

Кажется, это какая-то проблема с округлением.

Поскольку arc cos определяется только между -1 и 1, вам, вероятно, следует предварительно проверить его параметр или быть готовым к тому, что он может вернуть null, если что-то не так с расчетом.

person lp_    schedule 31.01.2015
comment
Теперь я заметил, что мои столбцы широты и долготы имеют тип varchar 255, поэтому они не плавают, поэтому они позволяют хранить более 8 чисел после десятичного числа. изменил тип на float, и теперь он работает, но я действительно не знаю, насколько он безопасен/точен. - person Tudor Ravoiu; 31.01.2015
comment
значение, хранящееся в varchar, преобразуется, так что это не проблема. пока вам не нужно сравнивать значения с плавающей запятой, я думаю, что здесь все в порядке. но имейте в виду, что это не решит проблему, связанную с тем, что acos может возвращать значение null. шансы очень малы, но у вас уже есть один в вашем примере. так что то же самое может повториться. - person lp_; 01.02.2015