在Oracle中使用变量循环查询

时间:2016-02-29 16:30:57

标签: sql oracle plsql

我根据日期查询,获取给定日期所需的数据(假设为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:

谁能告诉我如何做到这一点?

3 个答案:

答案 0 :(得分:1)

虽然bastihermann为您提供了在单个结果集中获取所有这些值的查询,但如果您想了解pl / sql块的问题,以下内容应该为您简化。该错误与以下事实有关:在pl / sql中,您需要选择INTO局部变量来包含要在代码中引用的数据。

纠正(并简化FOR LOOP)你的块:

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;

希望有所帮助

答案 1 :(得分: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)

答案 2 :(得分: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天。