Как перебрать диапазон дат в PL / SQL - PullRequest
8 голосов
/ 12 июня 2009

Мне нужно написать отчет, который генерирует итоговые итоги по таблице с диапазонами дат для каждой записи.

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

UPDATE:

В итоге я использовал пример здесь , чтобы выполнить то, что я хотел сделать. Это создает функцию, которая генерирует таблицу дат.

Ответы [ 6 ]

16 голосов
/ 12 июня 2009

Вам понадобится какой-то календарь, чтобы перебрать диапазон дат. Я построил один, используя подключение по уровню трюк. Затем вы можете присоединиться к календарю со своими данными (перекрестное объединение, так как вы хотите строку, даже если для этого дня нет опций):

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
14 голосов
/ 24 мая 2013

Одним из решений, которое я использую для этого, является преобразование диапазона дат в целочисленный диапазон, который можно использовать в цикле 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;
4 голосов
/ 23 сентября 2009

Точно так же, как дополнение к другим методам, я выполняю итерации по датам следующим образом:

/* 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
3 голосов
/ 10 июля 2013

Вот ответ, основанный на ответе выше: Используется дата начала и окончания:

В нем перечислены все дни с 01.07.2013 по 31.07.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;
0 голосов
/ 14 октября 2018
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;
0 голосов
/ 12 июня 2009

Этот тип запроса лучше всего обрабатывать, если у вас есть вторая «служебная» таблица, которую вы можете использовать практически для любого запроса, где вам нужно преобразовать диапазоны в определенные сегменты. Таблица утилит представляет собой не более чем список чисел:

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: я действительно не должен использовать BETWEEN для запросов диапазона дат - я изменил его на> = <</p>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...