2016-09-28 82 views
2

我正試圖解決在工作中的趨勢問題,非常類似於下面的示例。我想我有一個方法,但不知道如何在SQL中完成它。Oracle SQL趨勢MTD數據

輸入數據是:

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上,以產生從所述輸入輸出,而不依賴於它。這樣,當新的月份添加到輸入時,輸出更改而不編輯查詢。

TRENDS的邏輯將出現在每個唯一的LOC_ID上。趨勢將有三個值:第一個月的「新」RAINED爲「Y」,隨後的任何月份的「結轉」爲RAYED爲「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 

這種方式,產生輸出「趨勢」,我可以說:

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或更簡單的方法來解決我的整體問題,同時保持每個月的自動化?

感謝您閱讀所有這些! :)

+1

在這個例子中,loc_id = 2的趨勢是否正確? 5月下雨,4月不下雨,所以我預計5月份會有新消息。我錯了嗎? – Aleksej

+0

什麼版本的Oracle?在12c中你可以使用MATCH_RECOGNIZE。 – mathguy

+0

@Aleksej你是對的,但我在我的例子中混淆了輸入。我編輯它是正確的。 –

回答

1

在下面的SQL中有兩部分。

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

通過計算上述兩個,我們可以編寫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; 
+0

給我一點時間給這個鏡頭。我會讓你知道它是怎麼回事。謝謝! –

+0

這與我所需要的非常接近。但是,我發現新版本在某些情況下無法正確標記,因爲COUNT_LOCID_RAINED不正確。我會把答案放在細節中。 當您在分區級別的loc_id下雨並將其放入輸入時,您會返回哪個計數? –

+0

考慮一個新LOC_ID,3. loc_ID 3返回子查詢: MTD LOC_ID下雨PREV_RAINED COUNT_LOCID_RAINED 01-APR-16 \t \t 3Ñ\t 01-MAY-16 \t \t 3Ñ\t \tÑ3 01-JUN-16 \t \t 3ÿ\t \tñ2 01-JUL-16 \t \t 3ñ\t \tÝ3 01-AUG-16 \t \t 3ÿ\t n 這會擾亂趨勢字段,因爲COUNT_LOCID_RAINED不正確。主查詢將工作,如果COUNT_LOCID_RAINED是 –

1

這是一個老版本的解決方案。 WITH子句用於輸入數據;該解決方案在WITH子句之後立即啓動。

接下來我將介紹MATCH_RECOGNIZE解決方案,並將其添加到此答案中。使用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 
+0

謝謝!我想盡量避免做類似於第一個答案的事情,因爲每次添加新月份時都需要編輯查詢。我希望查詢自行適應新數據。 –

+0

@RyanBarker - ??新增月份時,查詢的哪些部分需要編輯?我希望你不是在談論WITH條款;正如我已經說過的那樣,解決方案不包括IT。刪除它,然後用剩下的('select mtd,...'結束)在你的表上(使用你的實際表和列名);當添加更多行時,查詢應該無任何變化地工作。 – mathguy

+0

我的歉意 - 我誤解了你所說的話。您的解決方案非常棒,而且運行非常好。感謝您提供,並再次表示歉意,我最初的誤解。 –

1

解(對於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;