I need to write a report that generates summary totals against a table with date ranges for each record.
table data:
option start_date end_date
opt1 6/12/2009 6/19/2009
opt1 6/3/2009 6/13/2009
opt2 6/5/2009 6/6/2009
What I want out is basically this:
date option count
6/1/2009 opt1 0
6/1/2009 opt2 0
6/2/2009 opt1 0
6/2/2009 opt2 0
6/3/2009 opt1 0
6/3/2009 opt2 1
I am having a hard time figuring out how to iterate over a date range. I am sure this is some simple cursor that could be created for this but I am at a loss. Preferably in PL/SQL
UPDATE:
I ended up using the example here to accomplish what I wanted to do. This creates a function that generates a table of dates.
Using while loop (better)
declare
dfrom date;
dtill date;
day date;
begin
dfrom := TO_DATE('09.09.1988', 'dd.mm.yyyy');
dtill := TO_DATE('19.09.1988', 'dd.mm.yyyy');
day := dfrom;
WHILE day <= dtill
LOOP
DBMS_OUTPUT.PUT_LINE(day);
day := day + 1;
END LOOP;
end;
/
//using cursor
declare
dfrom date;
dtill date;
begin
dfrom := TO_DATE('09.09.1988', 'dd.mm.yyyy');
dtill := TO_DATE('19.09.1988', 'dd.mm.yyyy');
FOR cur IN (
SELECT dfrom + LEVEL - 1 AS today
FROM dual
CONNECT BY LEVEL <= dtill - dfrom + 1
) LOOP
DBMS_OUTPUT.PUT_LINE(cur.today);
END LOOP;
end;
/
One solution that I use for this is to convert the date range into an integer range that you can use in a for loop, then convert back to a date to do stuff with it. You can't do any joins or anything this way, but it's a much smaller solution that those already posted:
declare
start_date number;
end_date number;
business_date varchar2(8);
begin
start_date := to_number(to_char(to_date('2013-04-25', 'yyyy-MM-dd'), 'j'));
end_date := to_number(to_char(to_date('2013-05-31', 'yyyy-MM-dd'), 'j'));
for cur_r in start_date..end_date loop
business_date := to_char(to_date(cur_r, 'j'), 'yyyy-MM-dd');
dbms_output.put_line(business_date);
end loop;
end;
You will need some sort of calendar to loop through a range of date. I have built one using the connect by level trick. You can then join the calendar with your data (cross join since you want a row even when there is no option for that day):
SQL> WITH calendar AS (
2 SELECT to_date(:begin_date, 'mm/dd/yyyy') + ROWNUM - 1 c_date
3 FROM dual
4 CONNECT BY LEVEL <= to_date(:end_date, 'mm/dd/yyyy')
- to_date(:begin_date, 'mm/dd/yyyy') + 1
5 )
6 SELECT c_date "date", d_option "option", COUNT(one_day)
7 FROM (SELECT c.c_date, d.d_option,
8 CASE
9 WHEN c.c_date BETWEEN d.start_date AND d.end_date THEN
10 1
11 END one_day
12 FROM DATA d, calendar c)
13 GROUP BY c_date, d_option
14 ORDER BY 1,2;
date option COUNT(ONE_DAY)
----------- ------ --------------
01/06/2009 opt1 0
01/06/2009 opt2 0
02/06/2009 opt1 0
02/06/2009 opt2 0
03/06/2009 opt1 1
03/06/2009 opt2 0
04/06/2009 opt1 1
04/06/2009 opt2 0
05/06/2009 opt1 1
05/06/2009 opt2 1
06/06/2009 opt1 1
06/06/2009 opt2 1
12 rows selected
Just as an addition to the other techniques, one way I iterate over dates is the following:
/* List of days for the past year, starting with today at midnight */
SELECT TRUNC(SYSDATE) + 1 - LEVEL AS today,
TRUNC(SYSDATE) + 2 - LEVEL AS tomorrow
FROM DUAL
CONNECT BY LEVEL <= 365
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With