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
Here is an answer based on an answer above: It uses a start and end date:
It lists all of the days of 07/01/2013 to 07/31/2013. Easily adaptable to any date range.
SELECT to_date('07/01/2013', 'mm/dd/yyyy') + LEVEL - 1 AS today
FROM dual
CONNECT BY LEVEL <= to_date('07/31/2013', 'mm/dd/yyyy') - to_date('07/01/2013', 'mm/dd/yyyy') + 1;
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;
/
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
This type of query is best handled if you have a second "utility" table, which you can use for just about any query where you need to convert ranges into specific buckets. The utility table is nothing more than a list of numbers:
CREATE TABLE Iterator (Counter NUMBER);
COUNTER
-------
0
1
2
3
...
100 (or however many rows you want to include)
IF we assume that you want to display 30 days, e.g.
SELECT TO_DATE('6/1/2009', 'MM/DD/YYYY') + i.counter thedate
, i.My_option
, count(y.My_option)
FROM ( SELECT DISTINCT
i2.Counter
, y.My_option
FROM iterator i2
, YourTable y
WHERE i2.Counter < 5
) i
LEFT OUTER JOIN yourtable y
ON TO_DATE('6/1/2009', 'MM/DD/YYYY') + i.counter
>= y.start_date
AND TO_DATE('6/1/2009', 'MM/DD/YYYY') + i.counter
< y.end_date
AND y.My_option = i.My_option
GROUP BY TO_DATE('6/1/2009', 'MM/DD/YYYY') + i.counter
, i.My_option
ORDER BY 1
, 2;
The idea is that you create a Cartesian product between your iterator table and your table with the range, then filter out all the cases where your range conditions aren't met. You can use this in many places, and is one of the best examples why it is better to model your data with ranges as opposed to discrete intervals - because you can always convert easily to discrete intervals using this technique.
edit: I really shouldn't use BETWEEN for date range queries - I changed it to >= <
declare
v_curr_date date;
for i in to_number(to_char(p_date_from ,'j')) .. to_number(to_char(p_date_to
,'j')) loop
v_curr_date = to_date(to_char(i),'j');
--make any operation on v_curr_date (like insert into table)
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;