【问题标题】:Aggregate outages' duration time for every day汇总每天的中断持续时间
【发布时间】:2019-06-23 20:15:54
【问题描述】:

我有一张表,用于存储有关某些设备的中断信息。在其他信息中,它包含beginTimeendTime,分别代表中断的开始和结束。

+----+---------------------+---------------------+-----+
| Id |      beginTime      |       endTime       | ... |
+----+---------------------+---------------------+-----+
| 10 | 13/01/2019 11:00:00 | 13/01/2019 15:00:00 |     |
| 20 | 13/01/2019 20:00:00 | 14/01/2019 09:00:00 |     |
| 30 | 13/01/2019 18:00:00 | 15/01/2019 10:00:00 |     |
| 40 | 16/01/2019 22:00:00 |                     |     |
+----+---------------------+---------------------+-----+

我想准备报告以显示每天的停机时间总和。因为表中有数百万条记录,所以我不想每次应用程序加载报告时都计算它,而是想将它存储在数据库中。我了解了物化视图,并认为这将是完成这项任务的好工具。我可以让它在一天结束时每天刷新。但是,我正在努力编写正确的 SQL。假设今天是 17-01-2019,想要的视图内容是这样的:

+------------+------+
|    date    | time |
+------------+------+
| 13/01/2019 |   14 |4 hours from 1st + 4 hours from 2nd + 6 hours from 3rd
| 14/01/2019 |   33 |9 hours from 2nd + 24 hours from 3rd
| 15/01/2019 |   10 |10 hours from 3rd
| 16/01/2019 |    2 |2 hours from 4th
+------------+------+

到目前为止我最好的尝试是

select to_char(nvl(endTime, current_timestamp),'YYYY-MM-DD') as date,
sum(time_diff(beginTime, nvl(endTime, current_timestamp))) as time
from ttest
group by to_char(nvl(endTime, current_timestamp),'YYYY-MM-DD');

time_diff 正在计算时间戳之间的差异。这显然是错误的,因为它基于endTime,但我现在被困在这里不知道去哪里。

那么,这可能吗?或者我应该使用标准表和一些 PL/SQL 来填充它?在这一点上,我还不知道考虑 PL/SQL 有哪些选择(比如每天触发它)。

【问题讨论】:

    标签: oracle plsql materialized-views


    【解决方案1】:

    我的尝试:

    select dt, 24 * sum(nvl2(endtime, least(dt + 1, endtime, dt + 1), dt) 
                      - nvl2(endtime, greatest(begintime, dt), begintime)) duration
      from ttest t
      join (select trunc(nvl(endtime, sysdate)) dt from ttest) d 
        on begintime < dt + 1 and (dt < endtime or endtime is null)
      group by dt order by dt  
    

    dbfiddle demo

    我用不同的日期进行了自我加入,然后我做了类似于你的总结。 endtime 中的空值由nvl2 处理,但您可以将其更改为case when。结果:

    DT            DURATION
    ----------- ----------
    2019-01-13          14
    2019-01-14          33
    2019-01-15          10
    2019-01-30         314
    

    根据需要,除了最后一行,因为计算是基于 sysdate,所以现在是 314 小时(但您可以将 sysdate 更改为任何日期,例如,如果您想测试 date '2019-01-17')。


    编辑:

    ...在这种情况下,我需要 17-01 的 24 小时记录,另外一个 18-01 24 小时,以此类推。

    所以你需要日期生成器:

    select dt + level - 1 dt 
      from (select trunc(min(endtime)) dt from ttest) 
      connect by dt + level - 1 < sysdate)
    

    将它与(稍加修改的)先前的查询结合起来:

    with 
      dates as (
        select dt + level - 1 dt 
          from (select trunc(min(endtime)) dt from ttest) 
          connect by dt + level - 1 < sysdate),
      details as (
        select dt, id, begintime, endtime,
               case when endtime is null then dt + 1 else least(dt + 1, endtime) end t2,
               greatest(begintime, dt) t1
          from ttest t join dates on begintime < dt + 1 and (dt < endtime or endtime is null))
    select dt, 24 * sum(t2 - t1) duration
      from details group by dt order by dt
    

    dbfiddle demo

    结果:

    DT            DURATION
    ----------- ----------
    2019-01-13          14
    2019-01-14          33
    2019-01-15          10
    2019-01-16           2
    2019-01-17          24
    2019-01-18          24
    ...                ...
    2019-01-30          24
    2019-01-31          24
    19 rows selected
    

    【讨论】:

    • 太棒了,但在这种情况下,我需要 17-01 的 24 小时记录,另一个 18-01 的 24 小时记录,依此类推。
    【解决方案2】:

    感谢@Ponder Stibbons 的回答,我设法找到了合适的选择:

    select dt, nvl(24 * sum(nvl2(endtime, least(dt + 1, endtime), dt+1) 
                      - greatest(begintime, dt)),0) duration
      from ttest t
      right join (select trunc((select min(beginTime) from ttest)) + rownum -1 dt
        from all_objects
          where rownum <= sysdate-cast((select min(beginTime) from ttest) as date)) d 
        on begintime < dt + 1 and (dt < endtime or endtime is null)
      group by dt
      order by dt
    

    demo

    我对从表中最早日期到当前日期的所有日期列表进行右连接并求和匹配记录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 2016-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-07
      • 2016-08-27
      相关资源
      最近更新 更多