AB Cadeが私を打ち負かしたようですが、私の解決策は次のとおりです。
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>