6

私はこれに夢中です。純粋なSQLでそれができたらいいのにと思いますが、この時点ではどのソリューションでもかまいません。

ほぼ同時に発生したイベントのリストを含むテーブルがありますta。目標は、 onから「孤立した」レコードを見つけることです。例えば:tbtatb

create table ta ( dt date, id varchar(1));
insert into ta values( to_date('20130101 13:01:01', 'yyyymmdd hh24:mi:ss') , '1' );
insert into ta values( to_date('20130101 13:01:02', 'yyyymmdd hh24:mi:ss') , '2' );
insert into ta values( to_date('20130101 13:01:03', 'yyyymmdd hh24:mi:ss') , '3' );


create table tb ( dt date, id varchar(1));
insert into tb values( to_date('20130101 13:01:5', 'yyyymmdd hh24:mi:ss') , 'a' );
insert into tb values( to_date('20130101 13:01:6', 'yyyymmdd hh24:mi:ss') , 'b' );

しかし、+-5 秒のしきい値を使用する必要があるとしましょう。したがって、検索するクエリは次のようになります。

  select
    ta.id ida,
    tb.id idb
  from
    ta, tb
  where 
    tb.dt between (ta.dt - 5/86400) and (ta.dt + 5/86400)
  order by 1,2 

(フィドル: http://sqlfiddle.com/#!4/b58f7c/5 )

ルールは次のとおりです。

  • イベントは 1 対 1 でマッピングされます
  • tb特定のイベントに最も近いイベントtaが正しいマッピングと見なされます。

つまり、結果のクエリは次のようなものを返す必要があります

IDA | IDB
1   | a
2   | b
3   | null  <-- orphan event

私がここに置いたサンプルクエリは、私が抱えている問題を正確に示しています。時間が重なると、体系的に正しい行を選択することが難しくなります。

dense_rank()正しい行を選択するための答えのようですが、どのパーティショニング/ソートがそれらを正しく配置しますか?

言及する価値がありますが、私はこれを Oracle 11gR2 で行っています。

4

1 に答える 1

2

これは、おそらく、row_number()、lag()、および max() オーバーのいくつかの組み合わせを使用して、Oracle の分析関数を使用する単一の SQL ステートメントで可能であるように思われます。しかし、私は単に頭を包むことができませんでした。ある分析関数を別の分析関数に組み込みたいとずっと思っていましたが、それはできないと思います。Common Table Expressions を使用して段階的に進めることができますが、それを機能させる方法がわかりませんでした。

しかし、結果を格納する追加の表とともに PL*SQL を使用する手続き型ソリューションはかなり単純です。row_number() を使用して、各ソース テーブルの各行に時系列のランクを割り当てます。明確な結果が必要なため、日時が重複している場合に備えてタイブレーカーを用意することが重要です。したがって、私の注文は dt, id です。ここにSQL-Fiddle のデモがあります。

または、以下のコードを見てください。

create table result ( 
  dif number, 
  ida varchar(1),
  idb varchar(1),
  dta date,
  dtb date
);

declare
  prevA integer := 0;
  prevB integer := 0;
begin
  for rec in (
    with 
    ordered_ta as (
      select dt dta,
             id ida,
             row_number() over (order by dt, id) rowNumA
        from ta
    ),
    ordered_tb as (
      select dt dtb,
             id idb, 
             row_number() over (order by dt, id) rowNumB 
        from tb
    )
    select ta.*,
           tb.*,
           abs(dta - dtb) * 86400 dif
      from ordered_ta ta
      join ordered_tb tb
        on dtb between (dta - 5/86400) and (dta + 5/86400)
     order by rowNumA, rowNumB
  )
  loop
    if rec.rowNumA > prevA and rec.rowNumB > prevB then
      prevA := rec.rowNumA;
      prevB := rec.rowNumB;
      insert into result values (
        rec.dif,
        rec.ida,
        rec.idb,
        rec.dta,
        rec.dtb
      );
    end if;
  end loop;
end;
/

select * from result
union all
select null dif, id ida, null idb, dt dta, null dtb
  from ta
 where id not in (select ida from result)
union all
select null dif, null ida, id idb, null dta, dt dtb
  from tb
 where id not in (select idb from result)
;
于 2013-06-16T17:31:44.757 に答える