给定一个包含4个圆的数据库,其中每个圆都有一个半径和一个地理定位的中心:
id | radius | latitude | longitude
---+--------+----------+----------
1 | 3 | 40.71 | 100.23
2 | 10 | 50.13 | 100.23
3 | 12 | 39.92 | 100.23
4 | 4 | 80.99 | 100.23
注意:为了使事情简单,每个圆的经度是相同的。
假设我们在第2个圆上,我想根据每个圆的
latitude
/longitude
坐标和radius
找到附近的每个圆。例如,根据纬度/经度坐标,我们有以下顺序:
9.42 <- 50.13 - 40.71
)10.21 <- 50.13 - 39.92
)30.86 <- 80.99 - 50.13
)但是根据纬度/经度坐标和每个圆的半径,我们应该有:
1.79 <- 12 - 10.21
)6.42 <- 9.42 - 3
)26.86 <- 30.86 - 4
)有没有一种简单的方法可以在SQL中执行此操作?
最佳答案
postgresql的contrib中提供的cube
和earthdistance
扩展名可以处理此操作,以至少产生近似答案。具体来说,他们假设地球是一个简单的球体,这使数学计算变得容易得多。
使用这些扩展名,您可以生成圆2和其他圆之间的距离,如下所示:
select circle.id,
earth_distance(ll_to_earth(circle.latitude, circle.longitude),
ll_to_earth(x.latitude, x.longitude))
from circle,
circle x
where x.id = 2 and circle.id <> x.id
order by 2;
校正圆半径应该只需要从上面的距离中减去
x.radius
和circle.radius
,尽管您需要考虑半径的单位。默认情况下,earth_distance
将以米为单位计算一个值。现在,让查询执行除扫描整个圈子列表并计算每个圈子的距离,然后对它们进行排序和限制之外的其他工作,这更具挑战性。有两种方法:
第二个选项主要从以下内容开始:
create table circle_distance as
select a.id as a_id, b.id as b_id,
earth_distance(ll_to_earth(a.latitude, a.longitude),
ll_to_earth(b.latitude, b.longitude))
from circle a, circle b
where a.id <> b.id;
alter table circle_distance add unique(a_id, b_id);
create index on circle_distance(a_id, earth_distance);
然后是一些相当繁琐的函数,用于在
circle_distance
中删除/插入相关行,由circle
上的触发器调用。这意味着您可以执行以下操作:select b_id from earth_distance where a_id = $circle_id order by earth_distance limit $n
该查询将能够使用
(a_id,earth_distance)
上的索引进行快速扫描。