2017-12-22 15 views
0

サブクエリが外部クエリの列を参照しているときに、PostgreSQLのビューをOracleに変換する際に問題があります。サブクエリの「無効な識別子」

この問題は何度かここで議論されているようですが、特定のクエリで動作する修正プログラムを入手できませんでした。

クエリの目的は、モバイルデバイスが最後に記録された位置を取得し、最も近いチェックポイント/ジオバンダーからKM単位で距離を取得し、3つの別個のテーブル:デバイス、デバイス位置、およびチェックポイントを参照することです。

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; 

私はしばらくの間、この上で立ち往生してきたと期待して誰かが、正しい道に感謝を私を置くことができます。

私は2つの問題を参照
+3

ここに余分なカンマがあります。「AS distance_to_checkpoint」、 –

+0

クエリは最後のデバイスの位置を取得しません。これは、最新のエントリである可能性がある「ID」ごとに最も高い「ROWID」を有するレコードを取得するが、保証されることは全くない。最後にどのポジションが実際に決定されますか? 「時間」欄では?そして、その名前にもかかわらず、 'device_locations'の' ID'はテーブルのIDではありませんか?代わりにデバイスIDですか?その名前は間違いなく変更する必要があります。そのサブクエリの 'ORDER BY'節は、まったく余計です。 –

+0

使用しているOracleのバージョンはどれですか? –

答えて

1

は、これは私の他の答えへのフォローアップです。最小距離のcheckpointsレコードを取得するには、テーブルに参加し、ウィンドウ関数を使用して最良のレコードを選択します。例えば:

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、と書くことが簡単です。

+0

これは私の問題を解決し、いくつかのことを学びました。ありがとうございました! – Nathan

1

  1. あなたの最終選択の列の後に余分なコンマ:AS distance_to_checkpoint,
  2. 外ではなく、派生テーブルdlの、内部表device_locations lを参照する列を選択 - 例:l.latitudedl.latitude
  3. する必要があります
+0

これらの問題は修正されましたが、大きな円の距離サブクエリに無効な識別子があります: ORA-00904: "DL" "LATITUDE":無効な識別子 00904. 00000 - "%s:無効識別子: *原因: *処置: 行のエラー:8列:192 – Nathan

1

まず最初に、クエリは最後のデバイスの位置を取得しません。最新のエントリである可能性がありますが、保証されているわけではありませんが、ごとに最高のROWIDのレコードを取得します。

あなたはおそらく範囲に関する問題があります。残念なことに、名前は1レベルだけ有効です。これは迷惑な制限です。 dl.latitudeなどはおそらくサブクエリ内でサブクエリであるため、サブクエリでは無効です。とにかく、あなたが得ようとしているのは、最小距離です。これは簡単にMINで得ることができます。

サブクエリ内のORDER BYは、標準SQLでは不必要です。オラクルはROWNUMテクニックの例外を作成していますが、これは使用しません。 (そして、言及したように、最小値を得るのも面倒です。)外部結合の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; 
+0

これは私の問題を解決したようですので、ご協力いただきありがとうございます。私は問合せを持っていますが、最も近いチェックポイントの緯度または経度を取得するにはどうしたらいいですか?私は範囲の問題に再びぶつかることなくそれをやり遂げる方法を考え出すことができませんでした。 – Nathan