Использование формулы Haversine с PostgreSQL и PDO - PullRequest
5 голосов
/ 08 августа 2011

На моем сайте я пытаюсь найти места поблизости.

Я пытаюсь использовать для этого формулу Хаверсайна.

Я использую следующий запрос, чтобы получить все местоположения в радиусе 25 км.

SELECT id, ( 6371 * acos( cos( radians(51.8391) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(4.6265) ) + sin( radians(51.8391) ) * sin( radians( lat ) ) ) ) AS distance
FROM shops
HAVING distance < 25
ORDER BY name asc

Однако я думаю, что некоторые функции могут быть только MySQL, потому что я получаю следующую ошибку:

Предупреждение: PDOStatement :: execute () [pdostatement.execute]: SQLSTATE [42883]: неопределенная функция: 7 ОШИБКА: радианы функции (текст) не существует LINE 1: ... id, (6371 * acos (cos (радианы (51.8391)) * cos (радианы (l ... ^ СОВЕТ: ни одна функция не соответствует заданному типу имени и аргумента. Возможно, вам потребуется добавить явное приведение типов. в ...

Или, возможно, это связано с тем, что мне нужно изменить текст lat в моем запросе. Но я не знаю, что это должно быть.

51.8391 и 4.6265 - это длина и широта моей «начальной» точки.

Любая помощь очень ценится, так как я понятия не имею, что изменить: -)

EDIT

Похоже, проблема в том, что я пытаюсь сделать: radians(lat).

lat - это столбец в моей таблице.

Когда я пытаюсь использовать rad(), как предположил Хакре, ошибка меняется на: function rad(numeric) does not exist

РЕДАКТИРОВАТЬ 2

Теперь мы куда-то добираемся.

Тип данных столбцов, которые действительно заданы как текст (как предполагает mu, слишком короткий).

Я изменил его на двойную точность.

Однако теперь я получаю еще одну ошибку:

Предупреждение: PDOStatement :: execute () [pdostatement.execute]: SQLSTATE [42703]: неопределенный столбец: 7 ОШИБКА: столбец «расстояние» не существует LINE 1: ... adians (lat)))) AS расстояние ОТ магазинов, имеющих расстояние <... ^ в ... </p>

Но я думал, что сделал псевдоним для выбора. Есть идеи?

Кроме того, если вы, ребята, думаете, что это должно перейти в другой вопрос, просто дайте мне знать, и я закрою этот вопрос.

Ответы [ 2 ]

7 голосов
/ 08 августа 2011

PostgreSQL имеет функцию radians:

radians(dp)
градусов в радианах

, но radians хочетаргумент с плавающей запятой, и вы пытаетесь задать ему какую-то строку:

Неопределенная функция: 7 ОШИБКА: радианы функции ( текст )
[...] СОВЕТ: ни одна функция не соответствует заданному имени и типам аргументов .Возможно, вам понадобится добавить явный тип приведений .

Выделение мое.Очевидно, ваши столбцы lat и lng являются столбцами char(n), varchar(n) или text.Вы должны исправить типы столбцов для lat и lng, чтобы они были numeric, float или какой-либо другой тип с плавающей запятой ;тем временем вы можете привести ваши строки вручную и надеяться, что у вас нет поврежденных данных:

radians(cast(lat as double precision))

MySQL делает много неявных преобразований типов, PostgreSQLболее строгий и требует, чтобы вы точно сказали, что вы имеете в виду.


Обновление для второй проблемы : Предложение HAVING оценивается перед предложением SELECT, поэтому псевдонимы столбцовв SELECT обычно недоступны где-либо еще в запросе.У вас есть пара вариантов, вы можете повторить свой большой уродливый Haversine:

SELECT id, ( 6371 * acos( cos( radians(51.8391) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(4.6265) ) + sin( radians(51.8391) ) * sin( radians( lat ) ) ) ) AS distance
FROM shops
HAVING ( 6371 * acos( cos( radians(51.8391) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(4.6265) ) + sin( radians(51.8391) ) * sin( radians( lat ) ) ) ) < 25
ORDER BY name asc

Или использовать производную таблицу, чтобы избежать повторения:

select id, distance
from (
    select id, name, ( 6371 * acos( cos( radians(51.8391) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(4.6265) ) + sin( radians(51.8391) ) * sin( radians( lat ) ) ) ) as distance
    from shops
) as dt
where distance < 25.0
order by name asc
2 голосов
/ 08 августа 2011

Преобразование в радианы тривиально:

radians(n) = n * PI / 180.0
...