2016-10-21 6 views
0

私は、次のフィールドとエントリを(日付はDD/MM/yyyy形式である)は、 "テスト" という名前のSQL(Oracleのヒキガエル)テーブルを、持っているとしますOracleのSQL(ヒキガエル):テーブルを展開し

id ref_date value 
--------------------- 
1 01/01/2014  20 
1 01/02/2014  25 
1 01/06/2014  3 
1 01/09/2014  6 
2 01/04/2015  7 
2 01/08/2015  43 
2 01/09/2015  85 
2 01/12/2015  4 

2014年2月と2014年6月のid = 1の値エントリがあるため、2014年3月から5月の値は0でなければならないことがわかります。idは2014年7月と8月に同じです= 1、2015年5月から7月、2015年10月から11月までid = 2の場合

ここで、特定のIDの値列の中央値を計算したい場合は、コーラルテーブルをそのまま使用して結果を整理してください - 私は各IDに対して5個のゼロエントリがないためです。

select id, median(value) as med_value from test group by id 
:私は、idで中央値を計算することができた上

私はそれゆえ、次の(潜在的にちょうど一時テーブル)を使用/作成したいと思います...

id ref_date value 
--------------------- 
1 01/01/2014  20 
1 01/02/2014  25 
1 01/03/2014  0 
1 01/04/2014  0 
1 01/05/2014  0 
1 01/06/2014  3 
1 01/07/2014  0 
1 01/08/2014  0 
1 01/09/2014  6 
2 01/04/2015  7 
2 01/05/2015  0 
2 01/06/2015  0 
2 01/07/2015  0 
2 01/08/2015  43 
2 01/09/2015  85 
2 01/10/2015  0 
2 01/11/2015  0 
2 01/12/2015  4 

...

どうすればいいですか?それとも別の方法がありますか?

多くのおかげで、

氏クルーレス

+1

あなたは[カレンダーテーブル](http://stackoverflow.com/questions/11576879/calendar-table-in-sql)が必要 – JohnHC

+0

は '日付をref_date'ていますか?そうであれば、フォーマットはありません。それとも特定の形式の 'varchar2'ですか? –

答えて

0

ref_dateが最新であると

with int1 as (select id 
        , max(ref_date) as max_date 
        , min(ref_date) as min_date from test group by id) 
    , s(n) as (select level -1 from dual connect by level <= (select max(months_between(max_date, min_date)) from int1)) 
select i.id 
    , add_months(i.min_date,s.n) as ref_date 
    , nvl(value,0) as value 
from int1 i 
join s on add_months(i.min_date,s.n) <= i.max_date 
LEFT join test t on t.id = i.id and add_months(i.min_date,s.n) = t.ref_date 

値と中央値

このソリューションで
with int1 as (select id 
        , max(ref_date) as max_date 
        , min(ref_date) as min_date from test group by id) 
    , s(n) as (select level -1 from dual connect by level <= (select max(months_between(max_date, min_date)) from int1)) 
select i.id 
    , MEDIAN(nvl(value,0)) as value 
from int1 i 
join s on add_months(i.min_date,s.n) <= i.max_date 
LEFT join test t on t.id = i.id and add_months(i.min_date,s.n) = t.ref_date 
group by i.id 
2

を有する第二の場合、私はテーブルを作成すべての "必要な日付"とすべての値が0です。次に、参加する代わりにunion allグループをidref_dateでグループ化し、各グループの値を追加します。日付に元のテーブルの値を持つ行がある場合は、結果の値です。そうでない場合、値は0になります。これにより、結合は回避されます。ほとんどすべての場合、union all +集約は、結合より高速です(場合によってははるかに高速です)。

さらに詳しいテストのために入力データを追加しました。あなたの元の質問では、あなたは2つのidを持っていて、両方とも4つの正の値を持っています。それぞれの場合に5つの値がないため、5つのゼロ(0)があり、どちらの場合も中央値が0であることを意味します。私が追加したid=3については、3つの正の値と3つのゼロがあります。中央値は最小の正の数の半分です。 id=4については、私はちょうど1つの値を持っています、そして、中央値でなければなりません。

解決策には、具体的な質問への答え、一時テーブルを作成する方法(一時テーブルではなく、インラインビュー)が含まれます。ファクト・サブクエリ(WITH句内)を使用すると、オプティマイザは、それらを一時表またはインライン・ビューとして扱うかどうかを決定します。 Explain Planを見てオプティマイザが何を決定したかを見ることができます。

with 
    inputs (id, ref_date, value) as (
     select 1, to_date('01/01/2014', 'dd/mm/yyyy'), 20 from dual union all 
     select 1, to_date('01/02/2014', 'dd/mm/yyyy'), 25 from dual union all 
     select 1, to_date('01/06/2014', 'dd/mm/yyyy'), 3 from dual union all 
     select 1, to_date('01/09/2014', 'dd/mm/yyyy'), 6 from dual union all 
     select 2, to_date('01/04/2015', 'dd/mm/yyyy'), 7 from dual union all 
     select 2, to_date('01/08/2015', 'dd/mm/yyyy'), 43 from dual union all 
     select 2, to_date('01/09/2015', 'dd/mm/yyyy'), 85 from dual union all 
     select 2, to_date('01/12/2015', 'dd/mm/yyyy'), 4 from dual union all 
     select 3, to_date('01/01/2016', 'dd/mm/yyyy'), 12 from dual union all 
     select 3, to_date('01/03/2016', 'dd/mm/yyyy'), 23 from dual union all 
     select 3, to_date('01/06/2016', 'dd/mm/yyyy'), 2 from dual union all 
     select 4, to_date('01/11/2014', 'dd/mm/yyyy'), 9 from dual 
    ), 
-- the "inputs" table constructed above is for testing only, 
-- it is not part of the solution. 
    ranges (id, min_date, max_date) as (
     select id, min(ref_date), max(ref_date) 
     from inputs 
     group by id 
    ), 
    prep (id, ref_date, value) as (
     select id, add_months(min_date, level - 1), 0 
     from ranges 
     connect by level <= 1 + months_between(max_date, min_date) 
       and prior id = id 
       and prior sys_guid() is not null 
    ), 
    v (id, ref_date, value) as (
     select id, ref_date, sum(value) 
     from  (select id, ref_date, value from prep union all 
        select id, ref_date, value from inputs 
       ) 
     group by id, ref_date 
    ) 
select id, median(value) as median_value 
from v 
group by id 
order by id -- ORDER BY is optional 
; 

ID MEDIAN_VALUE 
-- ------------ 
1   0 
2   0 
3   1 
4   9 
関連する問題