【问题标题】:How to iterate over a date range in PL/SQL如何在 PL/SQL 中迭代日期范围
【发布时间】:2010-11-02 12:33:39
【问题描述】:

我需要编写一份报告,针对每条记录的日期范围生成汇总表。

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 来完成我想做的事情。这将创建一个生成日期表的函数。

【问题讨论】:

    标签: sql database oracle plsql


    【解决方案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; /

    【讨论】:

      【解决方案2】:
      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;
      

      【讨论】:

        【解决方案3】:

        这是基于上述答案的答案: 它使用开始和结束日期:

        它列出了从 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;
        

        【讨论】:

          【解决方案4】:

          我为此使用的一个解决方案是将日期范围转换为可以在 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;
          

          【讨论】:

          • 工作正常 :) 注意它应该是:business_date varchar2(10);而不是 varchar2(8);
          • 营业日期错误。它应该是 varchar2(10)。请为其他用户更新!
          【解决方案5】:

          作为对其他技术的补充,我迭代日期的一种方法如下:

          /* 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
          

          【讨论】:

            【解决方案6】:

            如果您有第二个“实用程序”表,则最好处理这种类型的查询,您可以将其用于几乎任何需要将范围转换为特定存储桶的查询。实用程序表只不过是一个数字列表:

            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;
            

            这个想法是您在迭代器表和具有范围的表之间创建一个笛卡尔积,然后过滤掉所有不满足范围条件的情况。您可以在很多地方使用它,这是最好的例子之一,为什么最好用范围而不是离散间隔来建模数据 - 因为您总是可以使用这种技术轻松转换为离散间隔。

            编辑:我真的不应该将 BETWEEN 用于日期范围查询 - 我将其更改为 >=

            【讨论】:

              【解决方案7】:

              您将需要某种日历来循环浏览一系列日期。我使用connect by level 技巧构建了一个。然后,您可以使用您的数据加入日历(交叉加入,因为即使当天没有选项,您也想要一行):

              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
              

              【讨论】:

              • 这正是我想要的......甚至比我上面引用的文章还要好。谢谢!
              • +1 - 你的解决方案比我的解决方案更有效,因为它有额外的步骤来创建左连接基表。不知道在索引表的情况下会如何。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-09-26
              • 1970-01-01
              • 2013-03-20
              • 1970-01-01
              • 1970-01-01
              • 2016-05-09
              • 1970-01-01
              相关资源
              最近更新 更多