【问题标题】:How to find the nearest location points?如何找到最近的定位点?
【发布时间】:2021-11-27 08:22:46
【问题描述】:

我有一张表,其中包含 customer_number、customer_location(lat, long)、current_latched_tower_ID、tower_location(lat,long) 和距离(在 cx 位置和锁定的塔位置之间)。我试图找到一个离客户位置非常近的塔,而不是 current_latched_tower。

示例:一位客户当前锁定在一个塔(KA001)上,客户位置与锁定的塔位置之间的距离为 1.6KM。但是,还有另一个塔(KA002)非常靠近客户位置。因此,它的客户位置和最近的塔位置之间的距离是 1.3KM。

表格

customer_number   cx_lat       cx_long      tower_lat   tower_long  Latched_tower_ID   Distance
34532             6.897257333  79.86474533  6.890487    79.869199   CM0321             0.51477
43445             6.935598403  81.14939421  6.947618    81.160246   BD0010             1.2292
54365             6.866224     79.88215     6.896111    79.868611   CM0037             1.6216
52568             7.113198     80.037247    7.121666    80.028888   GM0121             0.9476

期待输出表

customer_number   cx_lat       cx_long      tower_lat   tower_long  Latched_tower_ID   Distance  Cloesed_tower_ID  Closed_Distance
34532             6.897257333  79.86474533  6.890487    79.869199   CM0321             0.51477   CM0037            0.43222
52568             7.113198     80.037247    7.121666    80.028888   GM0121             0.9476  NULL                NULL

如果没有更近的塔而不是锁定塔“Closed_tower_ID”和“Closed_Distance”列应该为NULL

【问题讨论】:

  • 你安装了sdo_geom 吗?我的意思是,距离是在应用程序端或数据库中计算的
  • 需要准确吗?如果不是,那么毕达哥拉斯方程将是小距离的第一个近似值(如果你只有最短距离而不是实际值)。否则看一下半正弦公式。

标签: oracle oracle11g


【解决方案1】:

如果你没有使用SDO_GEOM,那么你可以创建haversine函数来计算两个纬度/经度坐标之间的距离:

CREATE FUNCTION haversine_distance(
  lat1  IN NUMBER,
  long1 IN NUMBER,
  lat2  IN NUMBER,
  long2 IN NUMBER
) RETURN NUMBER DETERMINISTIC
IS
  PI           CONSTANT NUMBER := ASIN(1) * 2;
  R            CONSTANT NUMBER := 6371000; -- Approx. radius of the earth in m
  PHI1         CONSTANT NUMBER := lat1 * PI / 180;
  PHI2         CONSTANT NUMBER := lat2 * PI / 180;
  DELTA_PHI    CONSTANT NUMBER := (lat2 - lat1) * PI / 180;
  DELTA_LAMBDA CONSTANT NUMBER := (long2 - long1) * PI / 180;
  a NUMBER;
  c NUMBER;
BEGIN
  a := SIN(delta_phi/2) * SIN(delta_phi/2) + COS(phi1) * COS(phi2) *
          SIN(delta_lambda/2) * SIN(delta_lambda/2);
  c := 2 * ATAN2(SQRT(a), SQRT(1-a));
  RETURN R * c; -- in metres
END;
/

那么,我假设你的数据是第三范式:

CREATE TABLE towers (
  tower_id PRIMARY KEY,
  t_lat,
  t_long
) AS
SELECT 'CM0321', 6.890487, 79.869199 FROM DUAL UNION ALL
SELECT 'BD0010', 6.947618, 81.160246 FROM DUAL UNION ALL
SELECT 'CM0037', 6.896111, 79.868611 FROM DUAL UNION ALL
SELECT 'GM0121', 7.121666, 80.028888 FROM DUAL;

CREATE TABLE customers (
  customer_number PRIMARY KEY,
  cx_lat,
  cx_long,
  latched_tower_id
) AS
SELECT 34532, 6.897257333, 79.86474533, 'CM0321' FROM DUAL UNION ALL
SELECT 43445, 6.935598403, 81.14939421, 'BD0010' FROM DUAL UNION ALL
SELECT 54365, 6.866224000, 79.88215000, 'CM0037' FROM DUAL UNION ALL
SELECT 52568, 7.113198000, 80.03724700, 'GM0121' FROM DUAL;

ALTER TABLE customers ADD CONSTRAINT customers__lti__fk
  FOREIGN KEY (latched_tower_id) REFERENCES towers (tower_id);

然后,在 Oracle 12 中,您可以使用以下方法计算较近的塔:

SELECT c.*,
       TO_CHAR(
         HAVERSINE_DISTANCE(c.cx_lat, c.cx_long, t.t_lat, t.t_long)/1000,
         'FM999990.000'
       ) AS distance,
       ct.tower_id AS closer_tower_id,
       TO_CHAR(ct.distance, 'FM999990.000') AS closer_distance
FROM   customers c
       INNER JOIN towers t
       ON (t.tower_id = c.latched_tower_id)
       LEFT OUTER JOIN LATERAL(
         SELECT ct.*,
                HAVERSINE_DISTANCE(
                    c.cx_lat,
                    c.cx_long,
                    ct.t_lat,
                    ct.t_long
                )/1000 AS distance
         FROM   towers ct
         ORDER BY distance ASC
         FETCH FIRST ROW ONLY
       ) ct
       ON (ct.tower_id != c.latched_tower_id);

哪些输出:

CUSTOMER_NUMBER CX_LAT CX_LONG LATCHED_TOWER_ID DISTANCE CLOSER_TOWER_ID CLOSER_DISTANCE
43445 6.935598403 81.14939421 BD0010 1.795
54365 6.866224 79.88215 CM0037 3.644 CM0321 3.053
34532 6.897257333 79.86474533 CM0321 0.899 CM0037 0.445
52568 7.113198 80.037247 GM0121 1.318

在 Oracle 12 之前,您可以使用:

SELECT customer_number,
       cx_lat,
       cx_long,
       latched_tower_id,
       distance,
       CASE
       WHEN latched_tower_id != closer_tower_id
       THEN closer_tower_id
       END AS closer_tower_id,
       CASE
       WHEN latched_tower_id != closer_tower_id
       THEN closer_distance
       END AS closer_distance
FROM   (
  SELECT c.*,
         TO_CHAR(
           HAVERSINE_DISTANCE(c.cx_lat, c.cx_long, t.t_lat, t.t_long)/1000,
           'FM999990.000'
         ) AS distance,
         ct.tower_id AS closer_tower_id,
         TO_CHAR(
           HAVERSINE_DISTANCE(c.cx_lat, c.cx_long, ct.t_lat, ct.t_long)/1000,
           'FM999990.000'
         ) AS closer_distance,
         ROW_NUMBER() OVER (
           PARTITION BY c.customer_number
           ORDER BY HAVERSINE_DISTANCE(c.cx_lat, c.cx_long, ct.t_lat, ct.t_long)
         ) AS rn
  FROM   customers c
         INNER JOIN towers t
         ON (t.tower_id = c.latched_tower_id)
         CROSS JOIN towers ct
  ORDER BY
         customer_number,
         DISTANCE ASC
)
WHERE  rn = 1;

db小提琴here

【讨论】:

  • 嗨@MT0,我的数据库模式(客户,塔)中已经有这两个表。如何使用 FOREIGN KEY 喜欢这些表?
  • @ChulaxshanGunasegaram 抱歉,我无法理解您的问题。 “喜欢”这些表格是什么意思?
  • "ALTER TABLE customers ADD CONSTRAINT customers__lti__fk FOREIGN KEY (latched_tower_id) REFERENCES towers (tower_id);"此代码对我不起作用,因为数据库管理员已经创建了客户和 Tower 表。我只有选择权限。那么,如何才能在这两个表之间建立关系呢?
  • @ChulaxshanGunasegaram 如果表已经存在,则不需要添加约束;你只需要使用SELECT 查询。
猜你喜欢
  • 2015-11-26
  • 2019-10-09
  • 1970-01-01
  • 1970-01-01
  • 2011-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-18
相关资源
最近更新 更多