2016-02-29 214 views
1

一個變量循環查詢我已經基於與我得到我所需要的某一天數據的日期查詢(可以說SYSDATE-1):與甲骨文

SELECT TO_CHAR(START_DATE, 'YYYY-MM-DD') "DAY", 
    TO_CHAR(TRUNC(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),3600)/60),'FM00') || ':' 
    || TO_CHAR(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),60),'FM00') "DURATION (mm:ss)" 
FROM UI.UIS_T_DIFFUSION 
WHERE APPID IN ('INT', 'OUT', 'XMD','ARPUX') 
AND PSTATE = 'OK' 
AND TO_CHAR(START_DATE, 'DD-MM-YYYY') = TO_CHAR(sysdate-1, 'DD-MM-YYYY') 
AND ROWNUM <= 22 
GROUP BY TO_CHAR(START_DATE, 'YYYY-MM-DD'); 

給我這個(如預期):

╔════════════╦══════════╗ 
║ DAY  ║ DURATION ║ 
╠════════════╬══════════╣ 
║ 2016-02-28 ║  303║ 
╚════════════╩══════════╝ 

現在我試圖添加一個循環來獲得2015年10月10日以來的每一天的結果。服用點是這樣的:

╔═══════════╦══════════╗ 
║ DAY ║ DURATION ║ 
╠═══════════╬══════════╣ 
║ 2016-02-28║  303║ 
╠═══════════╬══════════╣ 
║ 2016-02-27║  294║ 
╠═══════════╬══════════╣ 
║  ...║  ...║ 
╠═══════════╬══════════╣ 
║ 2015-10-10║  99║ 
╚═══════════╩══════════╝ 

我試圖把查詢循環中:

DECLARE 
    i NUMBER := 0; 
BEGIN 
    WHILE i <= 142 
    LOOP 
    i := i+1; 
    SELECT TO_CHAR(START_DATE, 'YYYY-MM-DD') "DAY", 
    TO_CHAR(TRUNC(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),3600)/60),'FM00') || ':' 
    || TO_CHAR(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),60),'FM00') "DURATION (mm:ss)" 
    FROM UI.UIS_T_DIFFUSION 
    WHERE APPID IN ('INT', 'OUT', 'XMD','ARPUX') 
    AND PSTATE = 'OK' 
    AND TO_CHAR(START_DATE, 'DD-MM-YYYY') = TO_CHAR(sysdate - i, 'DD-MM-YYYY') 
    AND ROWNUM <= 22 
    GROUP BY TO_CHAR(START_DATE, 'YYYY-MM-DD'); 
    END LOOP; 
END; 

,但我得到這個錯誤:

Error report - 
ORA-06550: line 7, column 5: 
PLS-00428: an INTO clause is expected in this SELECT statement 
06550. 00000 - "line %s, column %s:\n%s" 
*Cause: Usually a PL/SQL compilation error. 
*Action: 

誰能告訴我怎麼樣完成這個?

回答

1

儘管bastihermann爲您提供了查詢以獲取單個結果集中的所有值,但如果您想了解pl/sql塊的問題,以下內容應該可以幫助您簡化它。該錯誤與這樣一個事實有關,在pl/sql中,您需要選擇INTO局部變量來包含代碼中引用的數據。

要糾正(與for循環簡化)的塊:

DECLARE 
    l_day  varchar2(12); 
    l_duration varchar2(30);; 
BEGIN 
    -- don't need to declare a variable for an integer counter in a for loop 
    For i in 1..142 
    LOOP 
    SELECT TO_CHAR(START_DATE, 'YYYY-MM-DD'), 
    TO_CHAR(TRUNC(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),3600)/60),'FM00') || ':' 
    || TO_CHAR(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),60),'FM00') 
    INTO l_Day, l_duration 
    FROM UI.UIS_T_DIFFUSION 
    WHERE APPID IN ('INT', 'OUT', 'XMD','ARPUX') 
    AND PSTATE = 'OK' 
    AND TO_CHAR(START_DATE, 'DD-MM-YYYY') = TO_CHAR(sysdate - i, 'DD-MM-YYYY') 
    AND ROWNUM <= 22 
    GROUP BY TO_CHAR(START_DATE, 'YYYY-MM-DD'); 
    -- and here you would do something with those returned values, or there isn't much point to this loop. 
    END LOOP; 
END; 

假設你需要做這些值的東西,希望更有效,你可以用遊標循環進一步簡化;

BEGIN 
    -- don't need to declare a variable for an integer counter in a for loop 
    For i_record IN 
    (SELECT TO_CHAR(START_DATE, 'YYYY-MM-DD') the_Day, 
    TO_CHAR(TRUNC(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),3600)/60),'FM00') || ':' 
    || TO_CHAR(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),60),'FM00') the_duration 
    FROM UI.UIS_T_DIFFUSION 
    WHERE APPID IN ('INT', 'OUT', 'XMD','ARPUX') 
    AND PSTATE = 'OK' 
    AND TO_CHAR(START_DATE, 'DD-MM-YYYY') <= TO_CHAR(sysdate, 'DD-MM-YYYY') 
    AND TO_CHAR(START_DATE, 'DD-MM-YYYY') >= TO_CHAR(sysdate-142, 'DD-MM-YYYY') 
    AND ROWNUM <= 22 
    GROUP BY TO_CHAR(START_DATE, 'YYYY-MM-DD') 
    ORDER BY to_char(start_date,'dd-mm-yyyy') 
    ) 
    LOOP 
    -- and here you would do something with those returned values, but reference them by record_name.field_value. 
    -- For now I will put in the NULL; command to let this compile as a loop must have at least one command inside. 
    NULL; 
    END LOOP; 
END; 

希望幫助

+0

感謝您的輸入。它確實有幫助。 – Svperstar

0

如果我得到你的權利,你不需要爲此循環。我想這會做的伎倆:

SELECT TO_CHAR(START_DATE, 'YYYY-MM-DD') "DAY", 
TO_CHAR(TRUNC(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),3600)/60),'FM00') || ':' 
|| TO_CHAR(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),60),'FM00') "DURATION (mm:ss)" 
FROM UI.UIS_T_DIFFUSION 
WHERE APPID IN ('INT', 'OUT', 'XMD','ARPUX') 
AND PSTATE = 'OK' 
AND TO_CHAR(START_DATE, 'DD-MM-YYYY') <= TO_CHAR(sysdate, 'DD-MM-YYYY') 
AND TO_CHAR(START_DATE, 'DD-MM-YYYY') >= TO_CHAR(sysdate-142, 'DD-MM-YYYY') 
AND ROWNUM <= 22 
GROUP BY TO_CHAR(START_DATE, 'YYYY-MM-DD'); 

在這裏你得到從現在到現在的每一天 - 142天。

+0

感謝您的輸入。你的查詢確實有效,但是它沒有得到我需要的數據。它將整體結果限制爲22行,而我需要將每日結果限制爲22行。 – Svperstar

1

首先你需要一個「日期生成」

select trunc(sysdate - level) as my_date 
from dual 
connect by level <= sysdate - to_date('10-10-2015','dd-mm-yyyy') 

MY_DATE 
---------- 
2016/02/28 
2016/02/27 
2016/02/26 
.... 
.... 
2015/10/12 
2015/10/11 
2015/10/10 

142 rows selected 

的,那麼你需要這個生成器插入到你的查詢

如果您正在使用Oracle 12c的這個非常容易在的幫助下橫向內嵌視圖

SELECT * 
FROM (
    select trunc(sysdate - level) as my_date 
    from dual 
    connect by level <= sysdate - to_date('10-10-2015','dd-mm-yyyy') 
) date_generator, 
LATERAL (
    /* your query goes here */ 
    SELECT TO_CHAR(START_DATE, 'YYYY-MM-DD') "DAY", 
    .... 
    AND START_DATE >= date_generator.my_date 
    AND START_DATE < date_generator.my_date + 1 
    AND ROWNUM <= 22 
    GROUP BY TO_CHAR(START_DATE, 'YYYY-MM-DD'); 
) 

如果您使用的是Oracle 11或10,它仍然可能,但更復雜;

SELECT TO_CHAR(START_DATE, 'YYYY-MM-DD') "DAY", 
    TO_CHAR(TRUNC(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),3600)/60),'FM00') || ':' 
    || TO_CHAR(MOD(ROUND(AVG((END_DATE - START_DATE)*86400),0),60),'FM00') "DURATION (mm:ss)" 
FROM (
     SELECT t.* , 
       row_number() over (partition by date_generator.my_date) rn 
     FROM UI.UIS_T_DIFFUSION t 
     JOIN (
       select trunc(sysdate - level) as my_date 
       from dual 
       connect by level <= sysdate - to_date('10-10-2015','dd-mm-yyyy') 
      ) date_generator 
     ON (t.UIS_T_DIFFUSION >= date_generator.my_date 
      AND t.UIS_T_DIFFUSION < date_generator.my_date + 1) 
     WHERE APPID IN ('INT', 'OUT', 'XMD','ARPUX') 
     AND PSTATE = 'OK' 
) 
WHERE rn <= 22 
GROUP BY TO_CHAR(START_DATE, 'YYYY-MM-DD'); 

的第一句話 - 當你不使用ORDER BY子句,查詢WHERE rownum <=22是不確定的 - 它可以在每次運行時返回不同的結果,因爲它根據自己的身體挑選從表22行在表中訂購。但是,行的物理順序可以隨時更改,除非使用ORDER BY子句,否則Oracle不保證任何順序,因此您的查詢....返回隨機結果。


二句話 - 從來不使用這個:

AND TO_CHAR(START_DATE, 'DD-MM-YYYY') = TO_CHAR(sysdate - i, 'DD-MM-YYYY') 

這樣可以防止數據庫使用指數,這可能會導致性能問題。
改爲使用此:

START_DATE >= trunc(sysdate - i) AND START_DATE < trunc(sysdate - i + 1) 
+0

非常感謝您的意見。 – Svperstar