Как выполнить итерацию по диапазону дат в PL / SQL
мне нужно написать отчет, который формирует сводные итоги в таблице с диапазонами дат для каждой записи.
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
то, что я хочу, в основном это:
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
мне трудно понять, как перебирать диапазон дат. Я уверен, что это какой-то простой курсор, который может быть создан для этого, но я в недоумении. Предпочтительно в PL / SQL
обновление:
Я закончил использование примера здесь выполнить что я хотел сделать. Это создает функцию, которая генерирует таблицу дат.
5 ответов
вам понадобится какой-то календарь для цикла через диапазон дат. Я построил один, используя подключение на уровне - трик. Затем вы можете присоединиться к календарю с вашими данными (перекрестное присоединение, так как вы хотите строку, даже если нет опции для этого дня):
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
одно из решений, которое я использую для этого, - преобразовать диапазон дат в целочисленный диапазон, который вы можете использовать в цикле for, а затем преобразовать обратно в дату, чтобы сделать с ней что-то. Вы не можете делать какие-либо соединения или что-то в этом роде, но это гораздо меньшее решение, которое уже опубликовано:
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;
как дополнение к другим методам, один из способов, которым я повторяю даты, следующий:
/* 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
вот ответ, основанный на ответ выше : Он использует дату начала и окончания:
в нем перечислены все дни с 07/01/2013 по 07/31/2013. Легко адаптируется к любому диапазону дат.
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;
этот тип запроса лучше всего обрабатывать, если у вас есть вторая таблица "утилита", которую вы можете использовать практически для любого запроса, где вам нужно преобразовать диапазоны в определенные ведра. Служебная таблица представляет собой не что иное, как список чисел:
CREATE TABLE Iterator (Counter NUMBER);
COUNTER
-------
0
1
2
3
...
100 (or however many rows you want to include)
если мы предположим, что вы хотите отобразить 30 дней, например
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;
идея заключается в том, что вы создаете Декартовое произведение между таблицей итератора и таблицей с диапазоном, а затем отфильтровываете все случаи, когда условия диапазона не выполняются. Вы можете использовать это во многих местах, и это один из лучших примеров, почему лучше моделировать ваши данные с диапазонами, а не дискретными интервалами - потому что вы всегда можете легко конвертировать в дискретные интервалы с помощью этой техники.
edit: я действительно не должен использовать между запросами диапазона дат - я изменил его на >=