Oracle 每天的总停机持续时间
我有一个表,用于存储某些设备的停机信息。除其他信息外,它还包含beginTime和endTime,分别表示停机的开始和结束Oracle 每天的总停机持续时间,oracle,plsql,materialized-views,Oracle,Plsql,Materialized Views,我有一个表,用于存储某些设备的停机信息。除其他信息外,它还包含beginTime和endTime,分别表示停机的开始和结束 +----+---------------------+---------------------+-----+ | Id | beginTime | endTime | ... | +----+---------------------+---------------------+-----+ | 10 | 13/01/2019
+----+---------------------+---------------------+-----+
| 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计算时间戳之间的差异。这显然是错误的,因为它是基于结束时间的,但我现在被困在这里,不知道去哪里
那么,这可能吗?或者我应该使用标准表和一些PL/SQL来填充它?现在,我还不知道我的选择是什么,比如每天触发PL/SQL。我的尝试:
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
如所需,除了最后一行,因为计算是基于sysdate的,所以现在是314小时,但您可以将sysdate更改为任何日期,例如,如果要测试,可以更改为日期“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)
多亏@Pound 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
我使用从表中最早的日期到当前日期和总和匹配记录的所有日期列表进行右联接。这太棒了,但在这种情况下,我需要一个17-01的24小时记录,另一个18-01的24小时记录,依此类推。
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
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
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