如何在Oracle中有效地计算坐标之间的距离

Posted

技术标签:

【中文标题】如何在Oracle中有效地计算坐标之间的距离【英文标题】:How to calculate distances between coordinates EFFICIENTLY in Oracle 【发布时间】:2010-12-08 03:49:15 【问题描述】:

我有一个大型 Oracle 数据库(大约有 720,000 条记录),其中每条记录都有自己的地理坐标(纬度和经度),我只需要选择距点特定距离(特定半径内)的记录.

目前我已经实现了一个距离函数(基于 hasrsine),我在一个 oracle 论坛中找到了该函数,但由于数据库有点大,每次选择大约需要 50 秒。

关于如何有效地做到这一点的任何建议?我知道有一个名为 oracle spatial & locator 的扩展,但我不知道我是否可以购买它,甚至不知道它是如何工作的。提前非常感谢。最好的问候

【问题讨论】:

【参考方案1】:

使用更好的算法。无需计算需要平方根计算的实际欧几里得距离,而是选择仅需要减法和加法的线性距离。 IE。如果您的点位于 (10, 10) 且半径为 5,则选择所有点位于由 (10 +/- 5, 10 +/- 5) 形成的正方形内的所有地点。

这将在正方形的角落捕获少量误报。通过计算适当的欧几里得距离来仔细检查应用程序中的结果来消除这些问题。

【讨论】:

最后我们认为这种方法是有效的,因为点之间的距离不是很大(只有一百米)。谢谢大家 还有一件事。为了提高效率,我们创建了两个索引,一个用于纬度列,另一个用于经度列。现在的表现真的很好。【参考方案2】:

如果您不需要距离太准确,您可以将地球视为平坦的。来自this discussion:

以英里为单位的大致距离:

sqrt(x * x + y * y)

其中 x = 69.1 * (lat2 - lat1) 和 y = 53.0 * (lon2 - lon1)

我最近对 ​​mysql 进行了一些优化(此处概述:www.mooreds.com/wordpress/archives/000547 [对不起,我每个帖子只有 1 个超链接])但不确定我经历了多少步骤适用于甲骨文。有些肯定是(比如尽可能使用边界框)。

【讨论】:

警告!上面的代码仅在特定参考纬度附近“起作用”,“魔术”系数是根据该参考纬度估计的。远离它的地区将是虚假的。【参考方案3】:

一些建议,如果你还没有这样做的话......

    由于 Haversine 计算需要以弧度为单位的角度,如果您以度为单位存储纬度和经度,请添加几列并预先计算弧度等值。更一般地,预先计算函数中您可以为公式计算的任何值并存储它们。

    考虑使用更简单的函数来消除半径之外的点,仅对基于更简单函数的潜在匹配点运行 Haversine 函数。对于度数,您可以使用 SQRT( (69.1*dLat)2 + (53*dLong)2) ) 并使用一些软糖因子 (10%)。如果您需要比简单计算提供的更好的结果,请仅在与粗略近似值匹配的点上运行 Haversine 计算。

【讨论】:

如果您将要搜索的半径平方,您也可以跳过 sqrt。 @Dolphin -- 我想我假设最终将需要实际距离作为输出的一部分,但如果不是,那么您可以简单地将距离平方以进行比较。【参考方案4】:

“特定距离”是否有点恒定? IE 您总是在搜索“1 英里内的所有点”还是半径会发生变化?

您希望在任何给定查询中返回总记录的百分比是多少? 10%? .10%?

如果您始终拥有相同的半径,请构建一个长度与半径相同的正方形网格。为每个人分配一个相邻方格的列表。每个点都会知道它所在的方格,从中可以得到所有相邻方格的列表。然后仅对这些正方形中的点运行计算。这类似于弹出的其他答案,但会更快,因为线性计算是在索引查找中近似的,而不是在每个点之间计算。

即使半径可变,您仍然可以使用上述方法,但您必须计算要包含多少“邻居”。仅当您希望从任何单个查询中获得总数的一小部分时,这些才是可行的。

【讨论】:

【参考方案5】:

请提供有关 Lat 和 Long 值的具体格式的更多详细信息,以及用于实现半正弦的具体公式。

有三种方法可以加快速度。根据具体情况,我们至少可以做到其中的两个。

    通过简单的属性值比较尽可能多地剔除记录。 对于这些记录,我们根本不需要计算任何东西。 例如,将最大半径要求转换为符合条件的经度(可能还有纬度)值的[宽泛但近似]范围

    使用替代(可能是近似的)距离测量方法。 例如,基于四舍五入的坐标计算欧几里得距离的平方可能会更快。 (当然要与所需半径的平方进行比较)

    改进半正弦公式的实现方式

【讨论】:

lat 和 long 是单独列中的浮点值。我使用的实现,我在这个论坛上找到了:forums.oracle.com/forums/thread.jspa?threadID=477747 ushitaki one。【参考方案6】:

如果您有许可证,那么 Oracle Spatial 可能会派上用场

Oracle Docs - Oracle Spatial

我没有使用过它,但是快速浏览一下文档会指向函数SDO_WITHIN_DISTANCE

【讨论】:

【参考方案7】:
Approximate distance in miles:

sqrt(x * x + y * y) 
where x = 69.1 * (lat2 - lat1) and y = 53.0 * (lon2 - lon1)

您可以获得更准确的结果...如果您更改 53.0 幻数...同时考虑纬度的变化。 (随着您向两极移动,逐渐变小。)

有没有人有那个神奇的魔法公式?

【讨论】:

这是平面近似,我认为它是:x = (lon2-lon1)*cos((lat1+lat2)/2.0); y = (lat2-lat1); d = 地球半径 * sqrt(xx + yy);其中 earthRadius 是以所需单位、公里或英里为单位的地球半径。 或者,更接近您使用的形式:x = 69.1 * (lat2 - lat1); y = 69.1 * (lon2 - lon1) * cos(lat1/57.3);来自meridianworlddata.com/Distance-Calculation.asp【参考方案8】:

首先,Haversine 并不完美,因为地球不是一个完美的球体 - 阅读http://www.movable-type.co.uk/scripts/latlong-vincenty.html

第二 - PL/SQL 不是一个完美的工具来编写多行代码的计算,这些代码将被多次调用。如果您使用 Java 或 C++ 来实现您的数学,您将获得巨大的性能提升。可以像函数一样从 Oracle 调用 C++ 或 Java 代码。

第三 - 那些评论说你需要用简单的矩形拳击尽可能多的点的人是非常正确的。通过经度和纬度列创建索引,这将有助于执行该装箱子句。

最后,我认为这里不必涉及 Oracle Spatial - 这是一种矫枉过正的做法。如果您已经拥有它并创建了 SDO_GEOMETRY 列,这是一个故事,但如果没有 - 我不会考虑。

【讨论】:

以上是关于如何在Oracle中有效地计算坐标之间的距离的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 data.table 有效地计算一个数据集中的 GPS 点与另一个数据集中的 GPS 点之间的距离

如何更有效地存储距离矩阵?

如何有效地计算时间之间的距离,不包括时间块?

一旦坐标在 Firebase 中保存为字符串,如何在 CLLLoationDegrees 中获取它们来计算用户之间的距离?

如何计算两个坐标之间的距离(没有浮点数)?

如何计算一组 x、y 坐标和位置变量之间的距离?