如何改进时间轴sql查询

时间:2014-08-12 09:56:15

标签: sql oracle

我必须构建一个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

任何想法,我如何重写查询?

1 个答案:

答案 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

希望这很有用: - )