Oracle'无效的标识符'在子查询中

时间:2017-12-22 15:27:21

标签: sql oracle oracle11g

当子查询引用外部查询中的列时,我遇到了将视图从PostgreSQL转换为Oracle的问题。

此问题似乎已在此处多次讨论,但我无法使用任何修复程序来处理我的特定查询。

查询的目的是让移动设备获得最后记录的位置,并从它的最近检查点/地理边界获得KM的距离,并且它引用3个单独的表:devices,device_locations和checkpoints。

SELECT 
    d.id,
    dl.latitude AS last_latitude,
    dl.longitude AS last_longitude,
    (SELECT * /* Get closest 'checkpoint' to the last device position by calculating the Great-circle distance */
    FROM (
        SELECT
            6371 * acos(cos(dl.latitude / (180/acos(-1))) * cos(checkpoints.latitude / (180/acos(-1))) * cos((checkpoints.longitude / (180/acos(-1))) - (dl.longitude / (180/acos(-1)))) + sin(dl.latitude  / (180/acos(-1))) * sin(checkpoints.latitude / (180/acos(-1)))) AS distance
        FROM checkpoints
        ORDER BY distance)
    WHERE ROWNUM = 1) AS distance_to_checkpoint
FROM devices d
LEFT JOIN ( /* Get the last position of the device */
    SELECT l.id,
        l.time,
        l.latitude,
        l.longitude,
        l.accuracy
    FROM device_locations l
    WHERE l.ROWID IN (SELECT MAX(ROWID) FROM device_locations GROUP BY id) 
    ORDER BY l.id, l.time DESC) dl 
ON dl.id = d.id;

我已经坚持了一段时间,并希望有人能把我放在正确的道路上,谢谢。

3 个答案:

答案 0 :(得分:1)

我看到两个问题:

  1. 最终选择列后的额外逗号:AS distance_to_checkpoint,
  2. 外部选择列引用内部表device_locations l,而不是派生表dl - 示例:l.latitude应为dl.latitude

答案 1 :(得分:1)

首先:查询没有获得最后的设备位置。它获取的ROWIDIDdl.latitude的记录可能恰好是最新的条目,但完全没有保证。

然后你很可能有范围问题。不幸的是,名称只有一个级别有效,这是一个恼人的限制。 MIN等在子查询中可能无效,因为它实际上是子查询中的子查询。无论如何,你想要获得的是最小距离,你可以轻松获得ORDER BY

子查询中的ROWNUM在标准SQL中是多余的。 Oracle对他们的ORDER BY技术做了例外,但我不会使用它。 (并且如上所述,获得最小值甚至是笨拙的。)无论如何,外连接中的select d.id as device_id, dl.latitude as last_latitude, dl.longitude as last_longitude, ( select min(6371 * acos(cos(dl.latitude / (180/acos(-1))) * cos(cp.latitude / (180/acos(-1))) * cos((cp.longitude / (180/acos(-1))) - (dl.longitude / (180/acos(-1)))) + sin(dl.latitude / (180/acos(-1))) * sin(cp.latitude / (180/acos(-1))) ) ) from checkpoints cp ) as distance from devices d left join ( select id as device_id, latitude, longitude, time, max(time) over (partition by id) as max_time from device_locations ) dl on dl.device_id = d.id and dl.time = dl.max_time; 是多余的。

这就是我解决问题的方法:

DataRetrieve

答案 2 :(得分:1)

这是我的另一个答案的后续行动。为了获得具有最小距离的checkpoints记录,您将加入表并再次使用窗口函数来选择最佳记录。 E.g:

select
  device_id,
  last_latitude,
  last_longitude,
  checkpoint_latitude,
  checkpoint_longitude,
  distance
from
(
  select
    device_id,
    last_latitude,
    last_longitude,
    checkpoint_latitude,
    checkpoint_longitude,
    distance,
    min(distance) over (partition by device_id) as min_distance
  from
  (
    select
      d.id as device_id,
      dl.latitude as last_latitude,
      dl.longitude as last_longitude,
      cp.latitude as checkpoint_latitude,
      cp.longitude as checkpoint_longitude,
      6371 *
      acos(cos(dl.latitude / (180/acos(-1))) *
           cos(cp.latitude / (180/acos(-1))) *
           cos((cp.longitude / (180/acos(-1))) - (dl.longitude / (180/acos(-1))))
           + 
           sin(dl.latitude / (180/acos(-1))) *
           sin(cp.latitude / (180/acos(-1)))
          ) as distance
    from devices d
    left join 
    (
      select 
        id as device_id, latitude, longitude, time,
        max(time) over (partition by id) as max_time
      from device_locations
    ) dl on dl.device_id = d.id and dl.time = dl.max_time
    cross join checkpoints cp
  )
)
where (distance = min_distance) or (distance is null and min_distance is null);

从Oracle 12c开始,CROSS APPLYOUTER APPLY可以更轻松地编写此类查询。