web-dev-qa-db-ja.com

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で

更新:

やりたいことを達成するために、例 here を使用することになりました。これにより、日付のテーブルを生成する関数が作成されます。

9
Adam Carr

日付の範囲をループするには、ある種のカレンダーが必要になります。 レベルで接続 トリックを使用して作成しました。次に、カレンダーをデータと結合できます(その日のオプションがない場合でも行が必要なため、相互結合)。

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
17
Vincent Malgrat

これに使用する1つの解決策は、日付範囲を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;
15
drakkanraz

他の手法への追加と同様に、日付を反復処理する1つの方法は次のとおりです。

/* 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
5
Sarah Vessels

上記の回答に基づく回答は次のとおりです。開始日と終了日を使用します。

2013年7月1日から2013年7月31日までのすべての日が一覧表示されます。任意の日付範囲に簡単に適応できます。

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;
3
bozone
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
Yasser Zilal

このタイプのクエリは、範囲を特定のバケットに変換する必要があるほぼすべてのクエリに使用できる2番目の「ユーティリティ」テーブルがある場合に最適に処理されます。ユーティリティテーブルは、数字のリストにすぎません。

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;

アイデアは、イテレータテーブルと範囲を持つテーブルの間にデカルト積を作成し、範囲条件が満たされないすべてのケースを除外することです。これは多くの場所で使用でき、離散間隔ではなく範囲を使用してデータをモデル化する方がよい理由の1つです。この手法を使用すると、いつでも簡単に離散間隔に変換できるためです。

編集:日付範囲のクエリにBETWEENを使用するべきではありません-> = <に変更しました

0
Steve Broberg