1

正如标题所说,我正在尝试计算许多经度和纬度点集之间的最短距离。我有一套房子和一套商店。对于每个家庭,我试图确定在 20 英里半径范围内哪个商店最近。

我编写的 SQL 可以正常工作,但是在为执行添加更多家时它不能很好地扩展。我正在尝试找到一种有效地进行此计算的方法。即使运行需要几个小时,我也会很高兴,因为我可以每月运行一次。但是,就目前而言,如果我针对数据库中的全部房屋进行尝试,此查询将运行数天。

到目前为止我尝试过的

  • 使用这个问题的指导,我利用 Oracle 的 SDO_GEOM 包来执行距离计算。
  • 在效率方面,我按照本指南中的建议在每个 long/lat 列上设置索引,并在 where 子句中设置代码以限制为 20 英里半径,以尝试立即过滤掉无效的 long/lat蝙蝠,从而减少了多余的计算。
  • 我可以为查询添加并行性,但我觉得这是一种减少运行时间的蛮力方法。虽然我认为补充并行性是可行的,但我想找到一个解决方案,让查询在我投入处理器之前高效运行。

数据设置

我正在开发一个具有两个数据集的 Oracle 19c 数据库:

1. HOME_ID 列表及其关联的经度和纬度

create table tmp_homes (
    home_id number not null,
    home_long float not null,
    home_lat float not null,
    primary key(home_id)
) nologging compress pctfree 0
;

该列表的大小可能有数十万条记录。

为每个 long/lat 列设置一个索引。

2. STORE_ID 列表及其关联的经度和纬度

create table tmp_stores (
    store_id number not null,
    store_long float not null,
    store_lat float not null,
    primary key(store_id)
) nologging compress pctfree 0
;

这个列表的大小大约是一千条记录。

为每个 long/lat 列设置一个索引。

询问

create table tmp_homes_to_stores compress nologging pctfree 0 as
select *
from (
    select
    h.home_id,
    s.store_id,
    sdo_geom.sdo_distance(
      sdo_geometry(2001, 4326, sdo_point_type(h.home_long, h.home_lat, null), null, null),
      sdo_geometry(2001, 4326, sdo_point_type(s.store_long, s.store_lat, null), null, null),
      0.01,
      'unit=KM'
    ) as distance,
    s.radius
    from tmp_homes h
    cross join (
        select store_id, store_long, store_lat, 32.1869 as radius, 111.045 as distance_unit, 0.0174532925 as deg2rad--, 57.2957795 as rad2deg
        from tmp_stores
    ) s
    where h.home_lat between s.store_lat - (s.radius / s.distance_unit) and s.store_lat + (s.radius / s.distance_unit)
    and h.home_long between s.store_long - (s.radius / (s.distance_unit * cos(s.deg2rad * (s.store_lat)))) and s.store_long + (s.radius / (s.distance_unit * cos(s.deg2rad * (s.store_lat))))
)
where distance <= radius -- 32.1869km = 20.00mi
;

如果我为少量记录运行此查询,它会很好地工作。不幸的是,当我对相当一部分工作数据进行测试时,它需要几个小时才能运行。我可以使用哪些修改或技巧来显着加快此查询的运行速度?

笔记

当前状态下的查询将返回与 20 英里半径内的 HOME_ID 关联的所有 STORE_ID。下一步是按每个 HOME_ID 的距离对输出进行排序,并选择到商店距离最短的记录。作为参考,该查询如下所示:

select home_id, store_id, distance
from (
    select
    hs.*,
    row_number() over(partition by home_id order by distance asc) as distance_rank
    from tmp_homes_to_stores hs
)
where distance_rank = 1
;
4

0 回答 0