You can use CONNECT BY to generate the list of days between end and start date and join this list with the list of rates:
with v_days as (
SELECT TRUNC (to_date('2014-04-05', 'YYYY-MM-DD') - ROWNUM + 1) dt
FROM DUAL
CONNECT BY ROWNUM <= (to_date('2014-04-05', 'YYYY-MM-DD') + 1 -
to_date('2014-03-30', 'YYYY-MM-DD'))
),
v_rates as (
select 1 rate_id, 'small' rate, 100 charge,
to_date('2014-01-01', 'YYYY-MM-DD') start_date,
to_date('2014-03-31', 'YYYY-MM-DD') end_date
from dual union all
select 2 rate_id, 'medium' rate, 200 charge,
to_date('2014-04-01', 'YYYY-MM-DD') start_date,
to_date('2014-04-04', 'YYYY-MM-DD') end_date from dual
union all
select 3 rate_id, 'big' rate, 300 charge,
to_date('2014-04-05', 'YYYY-MM-DD') start_date,
to_date('2014-12-31', 'YYYY-MM-DD') end_date from dual
)
select sum(charge) as total_charge from (
select d.*, r.* from v_days d
join v_rates r on d.dt >= r.start_date and d.dt <= r.end_date
order by d.dt
)
Explanation:
- v_days generates the list of days between the start and end date (one row per day)
- v_rates simply contains the rates you've given
- we then join these two subqueries - a given date belongs to a rate if it is between the start and end date of the rate
- at last, we just sum the charges to get the total charge