2009-06-12 31 views
6

我需要編寫一個報表,該報表根據每個記錄的日期範圍生成彙總總計。如何迭代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

UPDATE:

我最終使用的例子here來完成我想做的事。這將創建一個生成日期表的函數。

回答

14

您將需要某種日曆來遍歷日期範圍。我已經使用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 
+0

這確實是我想要的...甚至比我上面提到的文章更好。謝謝! – 2009-06-12 17:18:51

+0

+1 - 通過額外的步驟創建左連接基表,您的解決方案比下面的解決方案效率更高。不確定在索引表格的情況下如何。 – 2009-06-12 17:19:51

0

,如果你有第二個「效用」這種類型的查詢被最好的處理表,您可以將其用於任何需要將範圍轉換爲特定存儲桶的查詢。實用程序表只不過是一個數字列表:

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日期範圍查詢使用 - 我把它改爲> = <

4

,正如除了其它技術,一個我遍歷日期的方法如下:

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

我使用的一種解決方案是將日期範圍轉換爲您可以在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; 
3

這是基於一個答案的答案以上: 它使用一個開始和結束日期:

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