【问题标题】:Using the Haversine formula with PostgreSQL and PDO在 PostgreSQL 和 PDO 中使用 Haversine 公式
【发布时间】:2011-10-21 23:38:40
【问题描述】:

在我的网站上,我正在尝试获取附近的位置。

我正在尝试为此使用 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]: Undefined function: 7 ERROR: function radians(text) does not exist LINE 1: ...id, ( 6371 * acos( cos ( radians(51.8391) ) * cos( radians( l... ^ 提示:没有函数匹配给定的名称和参数类型。您可能需要添加显式类型转换。在 ...

或者这可能与我必须更改查询中的文本 lat 有关。但我不知道应该是什么。

51.8391 和 4.6265 是我的“起点”点的经纬度。

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

编辑

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

lat 是我表中的一列。

当我按照 hakre 的建议尝试使用 rad() 时,错误更改为:function rad(numeric) does not exist

编辑 2

现在我们正在取得进展。

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

我已将其更改为双精度。

但是现在我得到另一个错误:

警告:PDOStatement::execute() [pdostatement.execute]: SQLSTATE[42703]: Undefined column: 7 ERROR: column "distance" does not exist LINE 1: ...adians( lat ) ) ) AS distance FROM 商店距离 <... in ...>

但我以为我在选择中创建了别名。有什么想法吗?

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

【问题讨论】:

  • @hakre: 不走运。见编辑
  • 只是一个快速的镜头(对不起,它没有工作:)),看看如何在 postgresql 中定义函数,这样你就可以更容易(更快)在查询中应用它(也可以看看@ 987654324@).
  • 注意:这不是Haversine 公式——这是更简单的基于余弦的公式。 Haversine 要复杂得多,但除非使用高精度(例如双精度)算术,否则会更准确。
  • 我个人认为新问题应该是一个单独的问题,但这里已经有很多关于“拥有与在哪里”的问题

标签: php postgresql pdo haversine


【解决方案1】:

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

【讨论】:

  • 太棒了。看来这确实是问题所在。但是,我现在又遇到了另一个错误 :( 感谢您到目前为止的帮助!如果您愿意,或者如果您不想也很好,请编辑问题!:)
  • 非常感谢!我将使用第一个选项,因为公式已经在变量中。
  • 工作:2 * 6371 * asin(sqrt((sin(radians((lat2 - lat1) / 2))) ^ 2 + cos(radians(lat1)) * cos(radians(lat2) ) * (sin(弧度((lon2 - lon1) / 2))) ^ 2))
【解决方案2】:

转换为弧度很简单:

radians(n) = n * PI / 180.0

【讨论】:

  • 这可能与列(-data)有关吗?因为这是我认为需要做的事情。不过不确定...另请参阅我的编辑
  • PostgreSQL有一个radians函数,问题是latlng不是数字。
  • 当然 - 只需将弧度数据存储在您的数据库中,而不是度数。更好的是,看到这个答案:stackoverflow.com/questions/6409035/…
  • 就像@mu 所说 - 将数字存储为作为数字,而不是作为文本!!
  • @Alnitak: 是的,那只是我愚蠢:)
猜你喜欢
  • 2013-06-09
  • 2013-01-11
  • 1970-01-01
  • 1970-01-01
  • 2011-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多