我必须构建一个Oracle sql查询,该查询在最后一天以15分钟的块为单位聚合数据。 我找到了一个有效的解决方案,但我不确定,如果我把一个简单的问题弄得更复杂了 比它实际上。
为了简单起见,这是我想在报告中使用的表格:
select 1 as reportval, sysdate - 1/5 as dc from dual
union
select 3, sysdate - 1/3 from dual
union
select 4, sysdate - 1/5 from dual
union
select 5, sysdate - 1/4 from dual
所以我构建了一个查询来获取最后一天的时间表:
SELECT LEVEL
, CASE
WHEN CAST (
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'MI') AS NUMBER) BETWEEN 0
AND 14 THEN
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'ddmmyyyyHH24')
|| '00'
WHEN CAST (
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'MI') AS NUMBER) BETWEEN 15
AND 29 THEN
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'ddmmyyyyHH24')
|| '15'
WHEN CAST (
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'MI') AS NUMBER) BETWEEN 30
AND 44 THEN
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'ddmmyyyyHH24')
|| '30'
WHEN CAST (
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'MI') AS NUMBER) BETWEEN 45
AND 59 THEN
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'ddmmyyyyHH24')
|| '45'
END
AS timeinterval
FROM DUAL
CONNECT BY LEVEL <= 96
并将其与我的报告表相结合。
-- aggregate data for report
select sum(qry3.reportval), qry1.chunkid from
-- timeline
(SELECT LEVEL
, CASE
WHEN CAST (
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'MI') AS NUMBER) BETWEEN 0
AND 14 THEN
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'ddmmyyyyHH24')
|| '00'
WHEN CAST (
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'MI') AS NUMBER) BETWEEN 15
AND 29 THEN
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'ddmmyyyyHH24')
|| '15'
WHEN CAST (
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'MI') AS NUMBER) BETWEEN 30
AND 44 THEN
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'ddmmyyyyHH24')
|| '30'
WHEN CAST (
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'MI') AS NUMBER) BETWEEN 45
AND 59 THEN
TO_CHAR (SYSDATE - ( (60 / 86400) * LEVEL * 15)
, 'ddmmyyyyHH24')
|| '45'
END
AS chunkid
FROM DUAL
CONNECT BY LEVEL <= 96) qry1
-- report data
, (SELECT qry2.reportval
, CASE
WHEN CAST (TO_CHAR (qry2.dc, 'MI') AS NUMBER) BETWEEN 0
AND 14 THEN
TO_CHAR (qry2.dc, 'ddmmyyyyHH24')
|| '00'
WHEN CAST (TO_CHAR (qry2.dc, 'MI') AS NUMBER) BETWEEN 15
AND 29 THEN
TO_CHAR (qry2.dc, 'ddmmyyyyHH24')
|| '15'
WHEN CAST (TO_CHAR (qry2.dc, 'MI') AS NUMBER) BETWEEN 30
AND 44 THEN
TO_CHAR (qry2.dc, 'ddmmyyyyHH24')
|| '30'
WHEN CAST (TO_CHAR (qry2.dc, 'MI') AS NUMBER) BETWEEN 45
AND 59 THEN
TO_CHAR (qry2.dc, 'ddmmyyyyHH24')
|| '45'
END as chunkid
from (select 1 as reportval, sysdate - 1/5 as dc from dual
union
select 3, sysdate - 1/3 from dual
union
select 4, sysdate - 1/5 from dual
union
select 5, sysdate - 1/4 from dual)qry2) qry3
where qry1.chunkid = qry3.chunkid (+)
group by qry1.chunkid order by qry1.chunkid
任何想法,我如何重写查询?
答案 0 :(得分:4)
避免执行所有CASE和TO_CHAR的一种方法是使用INTERVAL DAY TO SECOND数据类型。您可以获得这样的时间表查询:
select level chunkid
, trunc(sysdate)
+ numtodsinterval(15 * (level-1), 'minute') interval_from
, trunc(sysdate)
+ numtodsinterval(15 * (level), 'minute')
- interval '1' second interval_to
from dual
connect by level <= 96
15分钟乘以等级-1给出每15分钟间隔的开始。
15分钟乘以等级然后减去1秒给出15分钟间隔的结束(至少在使用DATE时,如果你使用的是TIMESTAMP,它将不会像这样工作。)
然后您可以使用BETWEEN加入您的数据表:
with intervals as (
select level chunkid
, trunc(sysdate)
+ numtodsinterval(15 * (level-1), 'minute') interval_from
, trunc(sysdate)
+ numtodsinterval(15 * (level), 'minute')
- interval '1' second interval_to
from dual
connect by level <= 96
), datatable as (
select 1 as reportval, sysdate - 1/5 as dc from dual
union all
select 3, sysdate - 1/3 from dual
union all
select 4, sysdate - 1/5 from dual
union all
select 5, sysdate - 1/4 from dual
)
select i.chunkid
, sum(d.reportval)
from intervals i
left outer join datatable d
on d.dc between i.interval_from and i.interval_to
group by i.chunkid
order by i.chunkid
如果你使用的是TIMESTAMP而不是DATE,那么跳过在间隔查询中减去一秒,然后在连接中使用&gt; = interval_from和&lt;来代替BETWEEN。 interval_to。
如果你没有使用chunkid,你可以完全跳过它:
with intervals as (
select trunc(sysdate)
+ numtodsinterval(15 * (level-1), 'minute') interval_from
, trunc(sysdate)
+ numtodsinterval(15 * (level), 'minute')
- interval '1' second interval_to
from dual
connect by level <= 96
), datatable as (
select 1 as reportval, sysdate - 1/5 as dc from dual
union all
select 3, sysdate - 1/3 from dual
union all
select 4, sysdate - 1/5 from dual
union all
select 5, sysdate - 1/4 from dual
)
select i.interval_from
, sum(d.reportval)
from intervals i
left outer join datatable d
on d.dc between i.interval_from and i.interval_to
group by i.interval_from
order by i.interval_from
希望这很有用: - )