Кстати, как сказал MarcinJ, 41 - 6 - это 35, а не 33. Таким образом, ответ - 80, а не 78.
Следующее решение будет работать, даже если параметр даты не одинтолько день (1440 минут).Скажем, если параметром даты является месяц или даже год, это решение все равно будет работать.
Демонстрация в реальном времени: http://sqlfiddle.com/#!18/462ac/5
-- arranged the opening and closing downtime
with a as
(
select
DateOpen d, 1 status
from dt
union all
select
DateClosed, 2
from dt
)
-- don't compute the downtime from previous date
-- if the current date's status is opened
-- yet the previous status is closed
, downtime_minutes AS
(
select
*,
lag(status) over(order by d, status desc) as prev_status,
case when status = 1 and lag(status) over(order by d, status desc) = 2 then
null
else
datediff(minute, lag(d) over(order by d, status desc), d)
end as downtime
from a
)
select sum(downtime) as all_downtime from downtime_minutes;
Вывод:
| all_downtime |
|--------------|
| 80 |
Посмотрите, как это работает:
![enter image description here](https://i.stack.imgur.com/JFs7b.png)
Он работает путем вычисления времени простоя от предыдущего простоя.Не вычисляйте время простоя, если статус текущей даты открыт, а статус предыдущей даты закрыт, что означает, что текущее время не перекрывается.Неперекрывающееся время простоя обозначается нулем.
Для этого нового открытого времени простоя его время простоя изначально равно нулю, время простоя будет рассчитываться в последующие даты вплоть до его закрытия.
Может сделатькороче код, изменив условие:
-- arranged the opening and closing downtime
with a as
(
select
DateOpen d, 1 status
from dt
union all
select
DateClosed, 2
from dt
-- order by d. postgres can do this?
)
-- don't compute the downtime from previous date
-- if the current date's status is opened
-- yet the previous status is closed
, downtime_minutes AS
(
select
*,
lag(status) over(order by d, status desc) as prev_status,
case when not ( status = 1 and lag(status) over(order by d, status desc) = 2 ) then
datediff(minute, lag(d) over(order by d, status desc), d)
end as downtime
from a
)
select sum(downtime) from downtime_minutes;
Не особенно горжусь моим оригинальным решением: http://sqlfiddle.com/#!18/462ac/1
Что касается status desc
в order by d, status desc
, если DateClosed аналогичен DateOpen других простоев, status desc
сначала отсортирует DateClosed.
Для этих данных, где 8:00 присутствует в обоихDateOpened и DateClosed:
INSERT INTO dt
([ID], [DateOpen], [DateClosed], [Total])
VALUES
(1, '2019-04-01 07:00:00', '2019-04-01 07:50:00', 50),
(2, '2019-04-01 07:45:00', '2019-04-01 08:00:00', 15),
(3, '2019-04-01 08:00:00', '2019-04-01 08:45:00', 45);
;
Для аналогичного времени (например, 8:00), если мы не будем сначала сортировать закрытие перед открытием, то 7:00 будут вычисляться только до 7:50,вместо 8:00, так как время простоя 8: 00 - изначально нулевое.Вот как устроены и рассчитываются время простоя и закрытие, если на аналогичную дату нет status desc
, например, 8:00.Общее время простоя составляет всего 95 минут, что неправильно.Это должно быть 105 минут.
![enter image description here](https://i.stack.imgur.com/JrHKM.png)
Вот как это будет организовано и вычислено, если мы сначала отсортируем DateClosed перед DateOpen (используя status desc
) когда у них аналогичная дата, например, 8:00.Общее время простоя составляет 105 минут, что является правильным.
![enter image description here](https://i.stack.imgur.com/N0OnI.png)