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

4
我有一个大型的Oracle数据库(约720,000条记录),每个记录都有自己的地理坐标(纬度和经度),我需要选择距离某个点一定距离(在特定半径内)的记录。
目前,我已经实现了一个距离函数(基于haversine公式),但由于数据库有点大,每次查询需要花费约50秒。
您有什么关于如何高效处理此问题的建议吗?我知道有一个名为Oracle Spatial & Locator的扩展程序,但我不知道是否可以购买,甚至不知道它是如何工作的。非常感谢您的帮助。最好的问候。
8个回答

5
使用更好的算法。不要计算实际的欧几里得距离,因为这需要进行平方根计算,而是在仅需要减法和加法的线性距离上进行选择。例如,如果您的点位于(10,10),半径为5,则选择所有点在由(10 +/- 5,10 +/- 5)形成的正方形内的地方。
这将捕获少量的误报,位于正方形角落的位置。通过在应用程序中计算正确的欧几里得距离来消除这些误报。

最终我们采取了这种方法,因为点之间的距离并不是很大(只有几百米)。谢谢大家。 - Fgblanch
1
还有一件事。为了使其更有效率,我们创建了两个索引,一个用于纬度列,另一个用于经度列。现在性能非常好。 - Fgblanch

5
请提供有关Lat和Long值特定格式以及实施haversine的具体公式的更多细节。
有三种方法可以加快速度。根据情况,我们可以采用至少两种方法中的一种或两种。
1. 通过简单的属性值比较筛选出尽可能多的记录,对于这些记录,我们根本不需要计算任何东西。例如,将最大半径要求转换为符合条件的[慷慨但近似的]经度(和可能的纬度)范围。
2. 使用另一种(可能是近似的)距离测量方法。例如,基于四舍五入的坐标计算欧几里得距离的平方可能更快。当然,还需要将其与所需半径的平方进行比较。
3. 改进haversine公式的实现方式。

lat和long是分别存储在不同列中的浮点值。我使用的实现方法是在这个论坛上找到的: http://forums.oracle.com/forums/thread.jspa?threadID=477747其中使用了ushitaki的方法。 - Fgblanch

4

如果您还没有这样做,以下是一些建议...

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

  2. 考虑使用一个简单的函数来消除半径外部的点,仅对那些基于简单函数可能匹配的点运行Haversine函数。对于度数,您可以使用SQRT ((69.1 * dLat)2 + (53 * dLong)2))并使用一些调整因子(10%)。如果您需要比较精确的结果,则仅在与较粗的近似值匹配的点上运行Haversine计算。


2
如果你对正在搜索的半径进行平方,那么你可能也可以跳过sqrt。 - Dolphin
@Dolphin -- 我猜我假设最终实际距离将作为输出的一部分需要,但如果不需要,则可以仅对距离进行平方以进行比较。 - tvanfosson

3

2

"特定距离"是否有些恒定?即您是否总是在搜索 "范围内所有点1英里",还是半径会改变?

您预计在任何给定查询中将获得多少百分比的总记录?10%?.10%?

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

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


1

如果您不需要距离太精确,可以将地球视为平面。来自此讨论

Approximate distance in miles:

sqrt(x * x + y * y)

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

我最近对mysql进行了一些优化(在这里概述:www.mooreds.com/wordpress/archives/000547 [抱歉,每篇文章只能有1个超链接]),但不确定我所经历的步骤中有多少适用于Oracle。其中一些肯定是适用的(例如尽可能使用边界框)。


警告!上述代码仅在特定参考纬度周围“运作”,“神奇”的系数是基于此参考纬度估计的。对于远离该地区的区域,它将是虚假的。 - Laurent Grégoire

0
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 = earthRadius * sqrt(xx + yy); 其中earthRadius是地球半径,以所需单位公里或英里表示。 - jimhark
或者更接近你使用的形式:x = 69.1 * (lat2 - lat1); y = 69.1 * (lon2 - lon1) * cos(lat1/57.3); 来自http://www.meridianworlddata.com/Distance-Calculation.asp - jimhark

0

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

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

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

最后,我认为Oracle Spatial在这里没有必要 - 这是一种过度杀伤力的做法。如果您已经拥有它并创建了SDO_GEOMETRY列,则是另一回事,但如果没有 - 我不会考虑它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接