我根据日期查询,获取给定日期所需的数据(假设为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:
谁能告诉我如何做到这一点?
答案 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天。