在我的oracle DB中,我有一个包含两列的表'action',start_time和end_time(都是date)。我可以在几秒钟内查询每个操作的操作持续时间,如下所示:
select (end_time-start_time)*24*60*60 as actionDuration from action
我们有一个2小时的维护窗口,00:00 - 02:00。我想忽略在此窗口中发生的操作所经过的时间。
最后一个复杂的案例:行动持续时间可能跨越多个maint窗口
答案 0 :(得分:1)
如果我对评论的猜测是正确的,那么:
select a.id, max((end_time - start_time) * 1440) - sum(nvl((mend2 - mbeg2), 0) * 1440) duration
from (select id, start_time, end_time, mbeg, mend,
case when start_time between mbeg and mend then start_time else maint.mbeg end mbeg2,
case when end_time between mbeg and mend then end_time else maint.mend end mend2
from action a left join
(select to_date(:PSTART, 'yyyy-mm-dd hh24:mi:ss') + rownum - 1 mbeg,
to_date(:PSTART, 'yyyy-mm-dd hh24:mi:ss') + 2/24 + rownum - 1 mend
from dual connect by rownum < :PDAYS) maint
on (maint.mbeg between start_time and end_time) or (maint.mend between start_time and end_time)
-- this condition I forgot earlier
where not (start_time between mbeg and mend
and
end_time between mbeg and mend)
) a
group by a.id
order by a.id;
在这里你需要使用参数:
:PSTART
- 首次维护的日期和时间:PDAYS
- 您想要计算行动持续时间的天数; 现在查询以分钟为单位计算持续时间,如果您需要其他测量单位,请使用另一个数字而不是1440。
UPD 工作原理。
子查询maint
使用分层子句connect by
根据需要创建任意数量的行(等于从第一个操作到最后一个操作的天数)
然后我使用行动表进行左联接。加入条件 - 维护在操作中开始或停止。加入的结果 - 操作列表,其中每个操作都遵循其所有维护,以及操作是否与维护相交 - NULL
然后,如果在行动发生时发生维护的开始或结束。如果在维护期间开始操作,那么我将操作开始作为维护的开始(字段mbeg2
)
与维护结束时相同的事情(字段mend2
);结果 - 字段mbeg2和mend2包含同时发生操作和维护窗口的时间间隔(只是句点的交集)
然后我使用max
聚合函数计算操作长度。如果动作非常长且与许多窗口相交,则子查询中会有很多行,这就是我使用max
的原因(您也可以使用min
或avg
并获得相同的结果)
然后我计算所有减少的维护间隔的总和(交叉的结果)并从动作长度中减去这个总和
我希望现在很清楚。
答案 1 :(得分:0)
如果日期之间有很多时间,并且如果您正在运行多个记录的报告,这种方法可能会真正扼杀性能,但可能值得一看。它需要操作的主键。我会考虑在表中添加一列来存储已用时间,并在更新触发器中计算它。
当然替换主键列的正确名称:
with action as
(select id, starttime, endtime from action where id = :P_ACTION_ID)
select count(*) seconds from action
where to_number(to_char(starttime + ((level-1) / (24*60*60)) , 'HH24')) between 2 and 23
CONNECT BY LEVEL <= (endtime-starttime)*24*60*60;
答案 2 :(得分:0)
select (- case when start_time > to_date(to_char(start_time, 'YYYYMMDD') || '0200', 'YYYYMMDDHH24MI')
then start_time - to_date(to_char(start_time, 'YYYYMMDD') || '0200', 'YYYYMMDDHH24MI')
else 0 end
+ case when end_time > to_date(to_char(end_time, 'YYYYMMDD') || '0200', 'YYYYMMDDHH24MI')
then end_time - to_date(to_char(end_time, 'YYYYMMDD') || '0200', 'YYYYMMDDHH24MI')
else 0 end
+ (select sum(case when trunc(start_time) + level - 1/24/60/60 >
to_date(to_char(trunc(start_time) + level - 1/24/60/60, 'YYYYMMDD') || '0200', 'YYYYMMDDHH24MI')
then trunc(start_time) + level - 1/24/60/60
- to_date(to_char(trunc(start_time) + level - 1/24/60/60, 'YYYYMMDD') || '0200', 'YYYYMMDDHH24MI')
else 0 end)
from dual connect by level <= trunc(end_time) - trunc(start_time)
)) * 24 * 60 *60 as actionduration
from action;
我们的想法是以较小的间隔分割你的间隔:
第一天:[00:00,start_time] (带减号的第一种情况)
最后一天:[00:00,end_time] (第二个案例)
之间的日子:[00:00 - 23:59] (标量子查询)
答案 3 :(得分:0)
我知道我在这个问题上错过了这条船,但是我使用与之前的答案截然不同的方法将另一个答案放在一起,并且可以发布它。它不那么优雅(说实话就是彻头彻尾的丑陋),但它似乎有效并且速度非常快。操作开始和结束时间以产生三个单独查询的并集 - 一个用于整天,一个用于第一天,一个用于最后一天。整天可以乘以22小时(24减去维护窗口)来获得秒数。如果需要计算第一天和最后几天,开始和结束时间将向上舍入到02:00。
计算10年的时间需要1/10秒。没有使用CONNECT BY LEVEL
来生成行,因此这段时间并不重要。
我在测试时发布,使用虚拟CTE的记录:
with action as
(select 1 id
,to_date('16-nov-2014 01:33:32', 'DD-MON-YYYY HH24:MI:SS') starttime
,to_date('20-nov-2014 14:05:06', 'DD-MON-YYYY HH24:MI:SS') endtime
from dual
union
select 2 id
,sysdate starttime
,sysdate+365 endtime
from dual)
select id, sum(difference) difference from
(select id
,(case when endtime between trunc(endtime)
and trunc(endtime) + (2/24)
then trunc(endtime)
else endtime
end
-
case when endtime between trunc(endtime)
and trunc(endtime) + (2/24)
then trunc(endtime)
else trunc(endtime) + (2/24)
end) * 24*60*60 difference
from action
union
select id
,(trunc(endtime)-trunc(starttime+1))*22*60*60 difference
from action
union
select id
,(trunc(starttime+1) - case when starttime between trunc(starttime)
and trunc(starttime) + (2/24)
then trunc(starttime) + (2/24)
else starttime
end)*24*60*60 difference
from action)
group by id;
这是一个较长的版本,显示了三个查询的工作原理:
with action as
(select 1 id
,to_date('16-nov-2014 01:33:32', 'DD-MON-YYYY HH24:MI:SS') starttime
,to_date('20-nov-2014 14:05:06', 'DD-MON-YYYY HH24:MI:SS') endtime
from dual
union
select 2 id
,sysdate starttime
,sysdate+3650 endtime
from dual)
select id
,'Last day part' description
,to_char(case when endtime between trunc(endtime)
and trunc(endtime) + (2/24)
then trunc(endtime)
else trunc(endtime) + (2/24)
end, 'DD-MON-YYYY HH24:MI:SS') starttime
,to_char(case when endtime between trunc(endtime)
and trunc(endtime) + (2/24)
then trunc(endtime)
else endtime
end, 'DD-MON-YYYY HH24:MI:SS') endtime
,(case when endtime between trunc(endtime)
and trunc(endtime) + (2/24)
then trunc(endtime)
else endtime
end
-
case when endtime between trunc(endtime)
and trunc(endtime) + (2/24)
then trunc(endtime)
else trunc(endtime) + (2/24)
end) * 24*60*60 difference
from action
union
select id
,'Whole days' description
,to_char(trunc(starttime+1), 'DD-MON-YYYY HH24:MI:SS') starttime
,to_char(trunc(endtime), 'DD-MON-YYYY HH24:MI:SS') endtime
,(trunc(endtime)-trunc(starttime+1))*22*60*60 difference
from action
union
select id
,'First day part' description
,to_char(case when starttime between trunc(starttime)
and trunc(starttime) + (2/24)
then trunc(starttime) + (2/24)
else starttime
end, 'DD-MON-YYYY HH24:MI:SS') starttime
,to_char(trunc(starttime+1), 'DD-MON-YYYY HH24:MI:SS') endtime
,(trunc(starttime+1) - case when starttime between trunc(starttime)
and trunc(starttime) + (2/24)
then trunc(starttime) + (2/24)
else starttime
end)*24*60*60 difference
from action;