使用带有PostgreSQL和PDO的Haversine公式

时间:2011-08-07 20:43:33

标签: php postgresql pdo haversine

在我的网站上,我正试图在附近找到位置。

我正在尝试使用Haversine公式。

我正在使用以下查询来获取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错误:函数弧度(文本)不存在第1行:... id,(6371 * acos(cos) (弧度(51.8391))* cos(弧度(l ... ^ HINT:没有函数匹配给定的名称和参数类型。您可能需要添加显式类型转换。在......

或许它与我必须在我的查询中更改文本lat有关。但我不知道应该是什么。

51.8391和4.6265是我的“起点”的长点。

我非常感谢任何帮助,因为我不知道该改变什么: - )

修改

看起来问题出在我尝试的地方:radians(lat)

lat是我表格中的一列。

当我尝试使用rad()作为hakre建议错误更改为:function rad(numeric) does not exist

编辑2

现在我们到了某个地方。

确实设置为文本的列的数据类型(由mu建议的太短)。

我把它改为双精度。

但是现在又出现了另一个错误:

  

警告:PDOStatement :: execute()[pdostatement.execute]:SQLSTATE [42703]:未定义列:7错误:列“距离”不存在第1行:... adians(lat))))AS距离来自距离<... ^的商店......

但我以为我在选择中做了一个别名。有什么想法吗?

另外,如果你们认为这应该是另一个问题,请告诉我,我会关闭这个。

2 个答案:

答案 0 :(得分:7)

PostgreSQL确实有radians函数:

  

radians(dp)
  度数到弧度

但是radians想要一个浮点参数,你试图给它一些字符串:

  

未定义的功能:7错误:函数弧度(文本
  [...]提示:没有函数匹配给定的名称和参数类型。您可能需要添加显式类型转换

强调我的。显然,您的latlng列是char(n)varchar(n)text列。您应该将latlng的列类型固定为numericfloat或其他floating point type;与此同时,您可以手动cast your strings并希望您没有任何损坏的数据:

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

答案 1 :(得分:2)

转换为弧度非常简单:

radians(n) = n * PI / 180.0