2

以下の例と非常によく似た、職場でのトレンドの問題を解決しようとしています。メソッドはあると思いますが、SQL でそれを行う方法がわかりません。

入力データは次のとおりです。

MTD         LOC_ID  RAINED
1-Apr-16    1       Y
1-Apr-16    2       N
1-May-16    1       N
1-May-16    2       N
1-Jun-16    1       N
1-Jun-16    2       N
1-Jul-16    1       Y
1-Jul-16    2       N
1-Aug-16    1       N
1-Aug-16    2       Y

望ましい出力は次のとおりです。

MTD         LOC_ID  RAINED  TRENDS
1-Apr-16    1       Y       New
1-May-16    1       N       No Rain
1-Jun-16    1       N       No Rain
1-Jul-16    1       Y       Carryover
1-Aug-16    1       N       No Rain
1-Apr-16    2       N       No Rain
1-May-16    2       N       No Rain
1-Jun-16    2       N       No Rain
1-Jul-16    2       N       No Rain
1-Aug-16    2       Y       New

MTD に依存せずに、MTD の傾向を調べて、入力から出力を生成しようとしています。このようにして、新しい月が入力に追加されると、クエリを編集せずに出力が変更されます。

TRENDS のロジックは、一意の LOC_ID ごとに発生します。トレンドには 3 つの値があります。RAINED が「Y」の最初の月の「新規」、RAINED が「Y」の次の月の「キャリーオーバー」、RAINED が「N」の月の「降雨なし」です。

listagg を使用して中間ステップを導入することで、この問題を自動化したいと考えています。たとえば、LOC_ID = "1" の場合:

MTD         LOC_ID  RAINED  PREV_RAINED
1-Apr-16    1       Y       (null) / 0 / (I don't care)
1-May-16    1       N       Y
1-Jun-16    1       N       Y;N
1-Jul-16    1       Y       Y;N;N
1-Aug-16    1       N       Y;N;N;Y

このようにして、出力に「TRENDS」を生成するには、次のように言えます。

case when RAINED = 'Y' then
    case when not regexp_like(PREV_RAINED, 'Y', 'i') then
        'New'
    else
        'Carryover'
    end
else
    'No Rain'
end as TRENDS

私の問題は、一意の LOC_ID ごとに PREV_RAINED を生成する方法がわからないことです。LAG() ステートメントを組み合わせて、MTD ごとに LOC_ID オーダーで分割する必要があると感じていますが、必要なラグの数は月ごとに異なります。

PREV_RAINED を生成する簡単な方法、または毎月の自動化を維持しながら全体的な問題を解決する簡単な方法はありますか?

これをすべて読んでくれてありがとう!:)

4

3 に答える 3

1

以下の SQL には 2 つの部分があります。

(i) Calculating the ROWNUMBER value for rained attribute at loc_id,rained level.
(ii) Get the count at partition level loc_id,rained.

上記の 2 つを計算することで、要件に基づいて傾向を計算する CASE WHEN ロジックを記述できます。

SELECT mtd,
       loc_id,
       rained,
       CASE WHEN rained = 'N' THEN 'No Rain'
            WHEN rained = 'Y' AND rn = 1 THEN 'New'
            ELSE 'Carry Over'    
        END AS Trends       
  FROM
        ( 
            SELECT mtd,
                   loc_id,
                   rained,                   
                   ROW_NUMBER() OVER ( PARTITION BY loc_id,rained ORDER BY mtd ) AS rn,
                   COUNT(*) OVER ( PARTITION BY loc_id,rained ) AS count_locid_rained               
              FROM INPUT
              ORDER BY loc_id,mtd,rained,rn
         ) X;
于 2016-09-28T18:57:17.870 に答える
1

MATCH_RECOGNIZE を使用したソリューション (Oracle 12c のみ)。データセットでさまざまなソリューションをテストします。MATCH_RECOGNIZE は他のソリューションよりも大幅に高速である可能性があると言われていますが、これは多くの要因に依存します。

select loc_id, mtd, rained, trends
from input_data
  match_recognize (
    partition by loc_id, rained
    order by     mtd
    measures     mtd as mtd,
                 case when rained = 'N' then 'No Rain'
                      else case when match_number() = 1 then 'New' else 'Carryover' end
                      end as trends
    pattern (a)
    define a as 0 = 0
  )
order by loc_id, mtd;
于 2016-09-28T21:46:50.743 に答える
1

これが古いバージョンの解決策です。WITH 句は入力データ用です。ソリューションは WITH 句の直後から始まります。

次に MATCH_RECOGNIZE ソリューションに取り組みます。この回答に追加する場合があります。

with
     input_data ( mtd, loc_id, rained ) as (
       select to_date('1-Apr-16', 'dd-Mon-rr'), 1, 'Y' from dual union all
       select to_date('1-Apr-16', 'dd-Mon-rr'), 2, 'N' from dual union all
       select to_date('1-May-16', 'dd-Mon-rr'), 1, 'N' from dual union all
       select to_date('1-May-16', 'dd-Mon-rr'), 2, 'N' from dual union all
       select to_date('1-Jun-16', 'dd-Mon-rr'), 1, 'N' from dual union all
       select to_date('1-Jun-16', 'dd-Mon-rr'), 2, 'N' from dual union all
       select to_date('1-Jul-16', 'dd-Mon-rr'), 1, 'Y' from dual union all
       select to_date('1-Jul-16', 'dd-Mon-rr'), 2, 'N' from dual union all
       select to_date('1-Aug-16', 'dd-Mon-rr'), 1, 'N' from dual union all
       select to_date('1-Aug-16', 'dd-Mon-rr'), 2, 'Y' from dual
     )
select mtd, loc_id, rained,
       case rained when 'N' then 'No Rain'
                   else case when rn = 1 then 'New' 
                                         else 'Carryover' end
                   end  as trends
from ( select mtd, loc_id, rained, 
              row_number() over (partition by loc_id, rained order by mtd) rn
       from   input_data
)
order by loc_id, mtd
;

出力

MTD                     LOC_ID RAINED TRENDS  
------------------- ---------- ------ ---------
01/04/2016 00:00:00          1      Y New      
01/05/2016 00:00:00          1      N No Rain  
01/06/2016 00:00:00          1      N No Rain  
01/07/2016 00:00:00          1      Y Carryover
01/08/2016 00:00:00          1      N No Rain  
01/04/2016 00:00:00          2      N No Rain  
01/05/2016 00:00:00          2      N No Rain  
01/06/2016 00:00:00          2      N No Rain  
01/07/2016 00:00:00          2      N No Rain  
01/08/2016 00:00:00          2      Y New      

 10 rows selected
于 2016-09-28T19:01:20.447 に答える