Продолжительность совокупных отключений на каждый день - PullRequest
0 голосов
/ 30 января 2019

У меня есть таблица, в которой хранится информация о сбоях некоторых устройств.Среди другой информации он содержит beginTime и endTime, которые представляют начало и конец отключения соответственно.

+----+---------------------+---------------------+-----+
| 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 (например, ежедневный запуск).

Ответы [ 2 ]

0 голосов
/ 31 января 2019

Благодаря ответу @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

Я правильно соединюсь со списком всех датс самой ранней даты в моей таблице до текущей даты и соответствующих записей.

0 голосов
/ 30 января 2019

Моя попытка:

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

Я сделал самостоятельное объединение с разными датами,затем я подвел итог твоему.Нулевые значения в 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

Результат:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...