2012-04-25 33 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; 

此代码进行了测试,准确给你想要的结果。