Domanda

+-------+-----------+------+----------------------+----------------------+
|RATE_ID|DESCRIPTION|CHARGE|FROM_DATE             |TO_DATE               |
+-------+-----------+------+----------------------+----------------------+
|1      |small      |100   |01/01/2014 12:00:00 AM|31/03/2014 12:00:00 AM|
+-------+-----------+------+----------------------+----------------------+
|2      |mediam     |200   |01/04/2014 12:00:00 AM|04/04/2014 12:00:00 AM|
+-------+-----------+------+----------------------+----------------------+
|3      |big        |300   |05/04/2014 12:00:00 AM|31/12/2014 12:00:00 AM|
+-------+-----------+------+----------------------+----------------------+

Let the above a sample table of charges within a date range, I will have an input like
start_date = to_date('30/mar/2014','dd/mon/yyyy') and
end_date = to_date('05/apr/2014','dd/mon/yyyy').

So the input dates included in two days of charge 100 (rate_id = 1), 4 days of charge 200 and 1 day of charge 300 and a total of 1300.

Is there any simple method to find the number of days existing in the given range so that I can count the total_charge. As currently I am using PL/SQL for the above using loop to find the existence.

As a whole : from the input,

30 and 31st march belongs to small(100 charge) => 100* 2 = 200
1, 2, 3, 4 of april belongs to medium( 200 charge) => 200*4 = 800
5th april belongs to big ( 300 charge) => 300*1  = 300
so
the total:= 200 + 800 + 300 = 1300

Thanks in advance.

È stato utile?

Soluzione

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

Altri suggerimenti

The DATEDIFF() function returns the time between two dates.

SELECT DATEDIFF(day,'01-01-2014','31-03-2014') AS DiffDate

And consider the date format to get the correct result.

Please refer below link for further information http://www.w3schools.com/sql/func_datediff.asp

select sum(case
             when trunc(&start_date) <= trunc("to_date") AND
                  trunc(&end_date) >= trunc(from_date) then
              (least(trunc(&end_date), trunc("to_date")) -
              greatest(trunc(&start_date), trunc(from_date)) + 1) * charge
             else
              0
           end) total_charge
  from your_table;

The most simple approach is to find if your start_date and end_date cross the borders of specific period (when part in the case), then calculate the difference between two dates which gives you number of days (+1 is needed, consider if start date and end date are same, you still need to charge for 1 day) and then multiply it by respective charge.

If the table with date ranges is large then consider moving case condition to where:

select sum((least(trunc(&end_date), trunc("to_date")) -
           greatest(trunc(&start_date), trunc(from_date)) + 1)
            * charge) total_charge
  from your_table
 where trunc(&start_date) <= trunc("to_date")
   AND trunc(&end_date) >= trunc(from_date);

You would rather not use column name "TO_DATE" as long as there is conversion function with the same name.

with w as (
  select 100 charge, date '2014-01-01' from_date, date '2014-03-31' to_date from dual union all
  select 200 charge, date '2014-04-01' from_date, date '2014-04-04' to_date from dual union all
  select 300 charge, date '2014-04-05' from_date, date '2014-12-31' to_date from dual
),
x as (
  select 
    date '2014-03-30' start_period,
    date '2014-04-05'   end_period
  from
    dual
)
select
sum(
  w.charge * 
    case when x.start_period <= w.  to_date and
              x.  end_period >= w.from_date
    then
       case when x.end_period > w.to_date
            then w.to_date
            else x.end_period
       end
            -
       case when x.start_period < w.from_date 
            then w.from_date
            else x.start_period
       end
       +1
    else
        0
    end
)   result
from
  w cross join x;

AND TO_DATE(SYSDATE,'dd/mm/yyyy') BETWEEN TO_DATE(SYSDATE,'dd/mm/yyyy') AND TO_DATE(END_DATE,'dd/mm/yyyy') AND TO_DATE(START_DATE,'dd/mm/yyyy') <= TO_DATE(SYSDATE,'dd/mm/yyyy');

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top