2017-07-24 51 views
1

,我有以下數據:百分年到日(連續YTD)

ID |MPERIOD|FRDATE |FR 
===+=======+==========+== 
100|2017M01|01.01.2017|60 \    \    \ 
101|2017M01|02.01.2017|75 > YtD 2017M01 |    | 
103|2017M01|08.01.2017|48/    > Ytd 2017M02 | 
104|2017M02|06.02.2017|55     |    > YtD 2017M03 
105|2017M02|15.02.2017|63    /    | 
106|2017M03|18.03.2017|41         | 
107|2017M03|22.03.2017|71        /
...|.......|..........|.. 

我需要計算80%百分每個月和年初至今的(最高),其月(從開始到當前計算時刻)。

我用下面的SQL查詢:

SELECT DISTINCT mperiod, 
    ROUND(PERCENTILE_CONT(0.8) WITHIN GROUP (ORDER BY fr OVER (PARTITION BY mperiod),2) "80%_FR", 
    ROUND(PERCENTILE_CONT(0.8) WITHIN GROUP (ORDER BY fr OVER (PARTITION BY SUBSTR(mperiod,1,4)),2) "80%_FR_YtD" 
FROM mytable 
ORDER BY 1 

如果我運行月的最後一天,這個查詢時,我沒有數據,下一個月又那麼這將SQL正確計算YTD值。例如,如果我有前六個月的數據並且沒有第七個月的數據,並計算第六個月的數據,那麼使用年份分區OVER (PARTITION BY SUBSTR(mperiod,1,4)進行計算將計算出正確的YtD值。但如果本月之後有數據,它將包含在PARTITION BY中,並且不會計算到此刻。

如何計算YtD以前的月份!?例如,第三個月的YtD的計算應包括僅計算年度前三個月的計算,而不是所有月份的計算。

回答

1

由於您無法使用窗口子句或按PERCENTILE_CONT(boo!)中的列按其他順序添加,因此以下是實現您的目標的一種方法。注:它並不漂亮,我相信它不會很棒,但至少應該起作用!

WITH mytable AS (SELECT 100 ID, '2017M01' mperiod, to_date('01/01/2017', 'dd/mm/yyyy') frdate, 60 fr FROM dual UNION ALL 
       SELECT 101 ID, '2017M01' mperiod, to_date('02/01/2017', 'dd/mm/yyyy') frdate, 75 fr FROM dual UNION ALL 
       SELECT 103 ID, '2017M01' mperiod, to_date('08/01/2017', 'dd/mm/yyyy') frdate, 48 fr FROM dual UNION ALL 
       SELECT 104 ID, '2017M02' mperiod, to_date('06/02/2017', 'dd/mm/yyyy') frdate, 55 fr FROM dual UNION ALL 
       SELECT 105 ID, '2017M02' mperiod, to_date('15/02/2017', 'dd/mm/yyyy') frdate, 63 fr FROM dual UNION ALL 
       SELECT 106 ID, '2017M03' mperiod, to_date('18/03/2017', 'dd/mm/yyyy') frdate, 41 fr FROM dual UNION ALL 
       SELECT 107 ID, '2017M03' mperiod, to_date('22/03/2017', 'dd/mm/yyyy') frdate, 71 fr FROM dual UNION ALL 
       SELECT 108 ID, '2016M12' mperiod, to_date('22/12/2016', 'dd/mm/yyyy') frdate, 42 fr FROM dual UNION ALL 
       SELECT 109 ID, '2016M11' mperiod, to_date('22/11/2016', 'dd/mm/yyyy') frdate, 32 fr FROM dual), 
     unpckd AS (SELECT mt.ID, 
         mt.mperiod, 
         mt.frdate, 
         mt.fr, 
         CASE WHEN substr(mt.mperiod, -2) <= d.id THEN SUBSTR(mt.mperiod, 1, 5) || to_char(d.id, 'fm09') 
         END new_mperiod, 
         d.id dummy_id 
       FROM mytable mt 
         INNER JOIN (SELECT LEVEL ID 
            FROM dual 
            CONNECT BY LEVEL <= 12) d ON substr(mt.mperiod, -2) <= d.id), 
     res AS (SELECT mperiod, 
         new_mperiod, 
         ROUND(PERCENTILE_CONT(0.8) WITHIN GROUP (ORDER BY fr) OVER (PARTITION BY CASE WHEN mperiod = new_mperiod THEN mperiod END),2) fr_80, 
         ROUND(PERCENTILE_CONT(0.8) WITHIN GROUP (ORDER BY fr) OVER (PARTITION BY new_mperiod),2) fr_80_ytd 
       FROM unpckd) 
SELECT DISTINCT new_mperiod mperiod, 
       fr_80 "80%_FR", 
       fr_80_ytd "80%_FR_YtD" 
FROM res 
WHERE new_mperiod = mperiod 
ORDER BY 1; 

MPERIOD  80%_FR 80%_FR_YtD 
-------- ---------- ---------- 
2016M11   32   32 
2016M12   42   40 
2017M01   69   69 
2017M02  61.4  65.4 
2017M03   65  69.4 

這是通過在數字1到12(一年中的12個月)和mperiod的最後兩位數之間進行部分交叉連接而實現的。一旦我們有了,我們現在知道這些行所屬的整個ytd時期(例如,1號將匹配2017M01,2匹配2017M01和2017M02等),因此您現在可以爲此計算值生成一個標籤(我稱之爲new_mperiod)並使用它進行分區。

這顯然是效率低下的(因爲部分交叉連接會產生比一年中所不需要的數據所需的更多的行,這些數據在所有月份都沒有獲取,後來被濾除掉了,但我無法想象得更好這樣做的方式。