每天总计停机时间

时间:2019-01-30 14:08:01

标签: oracle plsql materialized-views

我有一个表,用于存储有关某些设备故障的信息。除其他信息外,它还包含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。假设今天是2019年1月17日,则所需的视图内容是这样的:

+------------+------+
|    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有什么选择(例如每天触发它)。

2 个答案:

答案 0 :(得分: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',如果要进行测试)。


编辑:

  

...在这种情况下,我需要24小时的17-01记录,另外一个   持续24小时等18-01。

因此您需要日期生成器:

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

答案 1 :(得分:1)

由于@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

我进行了右连接,并列出了从表中最早的日期到当前日期以及所有匹配记录的所有日期的列表。