トップページに戻る    次のSQLパズルへ    前のSQLパズルへ

10-326 最小上界な行がなければ、最大下界を取得その2

SQLパズル

NameT
ID   DateCol     Val
---  ----------  ----
111  2010-03-13  John
111  2010-03-17  Smith
222  2010-03-12  Tom
222  2010-03-18  Jane

TranT
ID   DateCol
---  ----------
111  2010-01-06
111  2010-03-12
111  2010-03-16
111  2010-03-20
111  2010-03-25
111  2010-04-20
222  2010-01-16
222  2010-03-12
222  2010-03-16
222  2010-04-20

NameTからValを取得したTranTを表示する。
取得するValは、同じIDでDateColをソートキーとしての、NameTの最小上界のValとする。
もし存在しなければ、同じIDでDateColをソートキーとしての、NameTの最大下界のValとする。

出力結果
ID   DateCol   Val
---  --------  -----
111  10-01-16  John
111  10-03-12  John
111  10-03-16  Smith
111  10-03-20  Smith
111  10-03-25  Smith
111  10-04-20  Smith
222  10-01-16  Tom
222  10-03-12  Tom
222  10-03-16  Jane
222  10-04-20  Jane

こちらを参考にさせていただきました(英語)


データ作成スクリプト

create table NameT(ID,DateCol,Val) as
select 111,date '2010-03-13','John'  from dual union all
select 111,date '2010-03-17','Smith' from dual union all
select 222,date '2010-03-12','Tom'   from dual union all
select 222,date '2010-03-18','Jane'  from dual;

create table TranT(ID,DateCol) as
select 111,date '2010-01-06' from dual union all
select 111,date '2010-03-12' from dual union all
select 111,date '2010-03-16' from dual union all
select 111,date '2010-03-20' from dual union all
select 111,date '2010-03-25' from dual union all
select 111,date '2010-04-20' from dual union all
select 222,date '2010-01-16' from dual union all
select 222,date '2010-03-12' from dual union all
select 222,date '2010-03-16' from dual union all
select 222,date '2010-04-20' from dual;


SQL

--■■■相関サブクエリを使う方法■■■
select ID,DateCol,
(select
 case max(case when b.DateCol >= a.DateCol then 1 else 0 end)
 when 1 then max(b.Val) Keep(Dense_Rank First order by case when b.DateCol >= a.DateCol
                                                             then 0 else 1 end,b.DateCol)
 else max(b.Val) Keep(Dense_Rank Last order by b.DateCol) end
   from NameT b
  where b.ID = a.ID) as Val
from TranT a
order by ID,DateCol;

--■■■内部結合を使う方法■■■
select ID,DateCol,Val
from (select a.ID,a.DateCol,b.Val,
      Row_Number() over(partition by a.ID,a.DateCol
                        order by b.DateCol) as rn
        from TranT a
        Join (select DateCol,Val,ID,
              max(DateCol) over(partition by ID) as maxData
                from NameT) b
          on a.ID = b.ID
         and (a.DateCol <= b.DateCol or b.DateCol=b.maxData))
where rn=1
order by ID,DateCol;


解説

相関サブクエリは、分かりやすいのですが、
分析関数を使ってから内部結合したほうが、パフォーマンスが良さそうですねぇ