2012-04-25 73 views
0

我有在表中的數據如下甲骨文:分解多個條目monthwise

start_date  end_date  amount 
15-01-2012  25-01-2012  100 
26-01-2012  10-02-2012  100 
11-02-2012  29-02-2012  100 

我想這些條目是分解monthwise如下

  • 15-01到25-01 = 100
  • 26-01至31-01 = 100 /((10-02-2012 - 26-01-2012)+1)*((31-01-2012 - 26-01-2012)+ 1)= 37.5 因此適用於15-01-2012至31-01-2012期間= 100 + 37.5 = 137.5

二月量的月應在

  • 01-02到10-02 = 100 /((10-02-2012 - 26-01-2012)+1)*((01-02- 2012 - 10-02-2012)+ 1)= 62.5
  • 11-02到29-02 = 100

所以對於二月01-02-2012到29-02-2012 = 62.5 + 100 = 162.5

這樣最終輸出應該是

start_date  end_date  amount 
15-01-2012 31-01-2012     137.5 
01-02-2012 29-02-2012   162.5 

沒有任何方法來實現這一目標而無需使用PLSQL

+1

對不起 - 我沒有看到你在哪裏得到你的價值像62.5? – Randy 2012-04-25 12:31:43

+0

請解釋得更好,使用僞代碼,不是直接值。併爲二月添加微積分。 – 2012-04-25 13:13:31

+0

你可以編輯你的問題。 – 2012-04-25 13:29:53

回答

1

您可以使用LAG函數來確定一個行和以前的(排序)行之間的日平均。

一旦你有一個日常的平均,你可以乘以該時期的天數。

全部在同一個sql語句中。

1

我不知道你想怎麼計算的值,但作爲一個開始,嘗試每月打破記錄:

with dts as (select last_day(add_months(
    to_date('20111201','yyyymmdd'),level)) ld, 
      add_months(
    to_date('20111201','yyyymmdd'),level) sd 
      from dual connect by level < 12) 
select case when start_date >= sd then start_date else sd end st_dt, 
case when end_date <= ld then end_date else ld end en_dt, amount, 
ld-sd days_in_month, 
case when end_date <= ld then end_date else ld end- 
case when start_date >= sd then start_date else sd end part 
from t, dts 
where (start_date >= sd and to_char(start_date, 'yyyymm') = 
     to_char(sd, 'yyyymm')) 
     or (end_date <= ld and to_char(end_date, 'yyyymm') = 
     to_char(ld, 'yyyymm')) 
1

我看到A.B.凱德打我,但這裏是我的解決方案:

SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MM-YYYY' 
    2/

Session altered. 

SQL> CREATE TABLE t (start_date DATE, end_date DATE, amount NUMBER); 

Table created. 

SQL> INSERT INTO t VALUES (TO_DATE('20120115','YYYYMMDD'),TO_DATE('20120125','YYYYMMDD'),100); 

1 row created. 

SQL> INSERT INTO t VALUES (TO_DATE('20120126','YYYYMMDD'),TO_DATE('20120210','YYYYMMDD'),100); 

1 row created. 

SQL> INSERT INTO t VALUES (TO_DATE('20120211','YYYYMMDD'),TO_DATE('20120229','YYYYMMDD'),100); 

1 row created. 

SQL> 

已經建立了一些測試數據...

SQL> COL for_month  FOR A9 
SQL> COL pro_rated_start FOR A15 
SQL> COL pro_rated_end FOR A13 
SQL> 

...並格式化一些列...

SQL> WITH months AS (
    2  SELECT TRUNC(MIN(start_date),'MM') min_start_month 
    3  ,  MONTHS_BETWEEN(TRUNC(MAX(end_date),'MM'),TRUNC(MIN(start_date),'MM')) + 1 mos 
    4  FROM t 
    5 ) 
    6 , offset AS (
    7  SELECT ROWNUM - 1 r 
    8  FROM (SELECT NULL 
    9    FROM DUAL 
10    CONNECT BY LEVEL <= (SELECT mos FROM months)) 
11 ) 
12 , ranges AS (
13  SELECT ADD_MONTHS(months.min_start_month, offset.r)    mo_start 
14  ,  LAST_DAY(ADD_MONTHS(months.min_start_month, offset.r))  mo_end 
15  FROM offset 
16  ,  months 
17 ) 
18 SELECT  TO_CHAR(GREATEST(t.start_date,ranges.mo_start),'Mon YYYY') for_month 
19 ,   MIN(GREATEST(t.start_date,ranges.mo_start))    pro_rated_start 
20 ,   MAX(LEAST(t.end_date,ranges.mo_end))      pro_rated_end 
21 ,   SUM(t.amount 
22     * CASE 
23     WHEN t.end_date < ranges.mo_end 
24      AND t.start_date > ranges.mo_start 
25     THEN 1 
26     ELSE ((LEAST(t.end_date,ranges.mo_end) 
27      - GREATEST(t.start_date,ranges.mo_start) + 1) 
28      /(t.end_date - t.start_date + 1)) 
29     END) pro_rated_amount 
30 FROM  t 
31 ,   ranges 
32 WHERE  t.start_date <= ranges.mo_end 
33 AND   t.end_date >= ranges.mo_start 
34 GROUP BY TO_CHAR(GREATEST(t.start_date,ranges.mo_start),'Mon YYYY'); 

FOR_MONTH PRO_RATED_START PRO_RATED_END PRO_RATED_AMOUNT 
--------- --------------- ------------- ---------------- 
Jan 2012 15-01-2012  31-01-2012    137.5 
Feb 2012 01-02-2012  29-02-2012    162.5 

SQL> 
1
SQL> select * from q10315606; 

START_DATE END_DATE  AMOUNT 
---------- ---------- ---------- 
2012-01-15 2012-01-25  100 
2012-01-26 2012-02-10  100 
2012-02-11 2012-02-29  100 

SQL> with day_generator as 
    2 -- this block expands out every date in the entire date range 
    3 (
    4 select min_start_date + level - 1 as date_value 
    5 from (select min(start_date) as min_start_date, 
    6     max(end_date) - min(start_date) + 1 as total_days 
    7   from q10315606 
    8  ) 
    9 connect by level <= total_days 
10 ), 
11 range_averages as 
12 -- this block generates a daily average for each date range 
13 (
14 select start_date, end_date, 
15   end_date - start_date + 1 as days_inclusive, 
16   amount/(end_date - start_date + 1) as daily_amount 
17 from q10315606 
18 ) 
19 select start_date, end_date, sum(amount) as amount 
20 from (
21 -- here we cast all the dates to the minimum and maximum value found in the month 
22   select min(dg.date_value) 
23     over (partition by last_day(dg.date_value)) as start_date, 
24     max(dg.date_value) 
25     over (partition by last_day(dg.date_value)) as end_date, 
26     ra.daily_amount as amount 
27   from range_averages ra, 
28     day_generator dg 
29   where dg.date_value between ra.start_date and ra.end_date 
30  ) 
31 group by start_date, end_date 
32* order by start_date, end_date 
[email protected]:SQL>/

START_DATE END_DATE  AMOUNT 
---------- ---------- ---------- 
2012-01-15 2012-01-31  137.5 
2012-02-01 2012-02-29  162.5 
1

這是一個嘗試:

我有一個加入星期一部份。如果一個行在兩個月內將會「重複」,更準確地說將在兩個月內分發。 對於每一行*月我套用公式amount/(days in current row) * (days in current month)

with months as 
    (select add_months('1-Jan-2012', level -1) as mon 
    from dual 
    connect by level < 12) 
select mon, sum(amount) 
from(
    select 
     months.mon, 
     amount/(end_date - start_date +1)* 
      (case when trunc(start_date,'MM') = months.mon 
       then least(last_day(start_date), end_date) - start_date +1 
       else end_date - greatest(months.mon, start_date) +1 
      end) as amount 
    from your_table y 
    join months on 
     (months.mon between trunc(start_date,'MM') and trunc(end_date, 'MM')) 
) 
group by mon; 

此代碼進行了測試,準確給你想要的結果。