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

5-57 前月が2位以内のデータを出力

SQLパズル

ValTable
Code  yyyymm  Val
----  ------  ---
AAAA  200704  300
BBBB  200704  200
CCCC  200704  100
AAAA  200705  400
BBBB  200705  500
CCCC  200705  600
AAAA  200706  900
BBBB  200706  800
CCCC  200706  700
DDDD  200712  100
EEEE  200712  200
FFFF  200712  300
DDDD  200801  400
EEEE  200801  500
FFFF  200801  600
GGGG  200810  900
GGGG  200812  800

まず、yyyymmごとに、上位2位までを抽出します。

Code  yyyymm  Val  Rank
----  ------  ---  ----
AAAA  200704  300     1
BBBB  200704  200     2
BBBB  200705  500     2
CCCC  200705  600     1
AAAA  200706  900     1
BBBB  200706  800     2
EEEE  200712  200     2
FFFF  200712  300     1
GGGG  200810  900     1
GGGG  200812  800     1

抽出した結果の、Codeが等しい1ヶ月後のデータを出力する。

出力結果
Code  yyyymm  Val
----  ------  ---
AAAA  200705  400
BBBB  200705  500
BBBB  200706  800
CCCC  200706  700
EEEE  200801  500
FFFF  200801  600


データ作成スクリプト

create table ValTable as
select 'AAAA' as Code,200704 as yyyymm,300 as Val from dual
union select 'BBBB',200704,200 from dual
union select 'CCCC',200704,100 from dual
union select 'AAAA',200705,400 from dual
union select 'BBBB',200705,500 from dual
union select 'CCCC',200705,600 from dual
union select 'AAAA',200706,900 from dual
union select 'BBBB',200706,800 from dual
union select 'CCCC',200706,700 from dual
union select 'DDDD',200712,100 from dual
union select 'EEEE',200712,200 from dual
union select 'FFFF',200712,300 from dual
union select 'DDDD',200801,400 from dual
union select 'EEEE',200801,500 from dual
union select 'FFFF',200801,600 from dual
union select 'GGGG',200810,900 from dual
union select 'GGGG',200812,800 from dual;


SQL

--■■■add_months関数を使う方法■■■
select Code,yyyymm,Val
from (select Code,yyyymm,Val,
      case when to_number(to_char(add_months(to_date(to_char(yyyymm),'yyyymm'),-1),'yyyymm'))
              = Lag(yyyymm) over(partition by Code order by yyyymm)
            and Lag(Rank) over(partition by Code order by yyyymm) <= 2
           then 1 else 0 end as WillOut
        from (select Code,yyyymm,Val,
              Rank() over(partition by yyyymm order by Val desc) as Rank
              from ValTable))
where WillOut = 1;

--■■■12進数変換を使う方法■■■
select Code,yyyymm,Val
from (select Code,yyyymm,Val,
      max(rn) over(partition by Code
      order by trunc(yyyymm/100)*12+mod(yyyymm,100)
      range between 1 preceding
                and 1 preceding) as PreRn
      from (select Code,yyyymm,Val,
            Rank() over(partition by yyyymm order by Val desc) as rn
              from ValTable))
where PreRn <=2
order by Code,yyyymm;


解説

add_months関数を使うより、12進数変換を使うほうがシンプルでしょう。