SQL Запрос на получение последних 24 часов подряд с потенциально пропущенными часами - PullRequest
0 голосов
/ 17 июня 2020

Итак, мне нужно вернуть из запроса последние 24 часа подряд. Таблица содержит почасовые данные. Получить последние 24 часа не проблема, но иногда мне не хватает данных, и поэтому мне нужно go назад во времени, чтобы найти первый «полный» набор из 24 часов.

select date, value from TABLE
where date >= (select max(date)-1 from TABLE) 

Однако иногда мне не хватает часов на этот запрос. Как я могу гарантировать, что я всегда получу 24 строки назад и что это будет самый последний блок за 24 часа?

Пример ниже:

enter image description here

Уведомление для категории A, час 1/31/2020 час 23 отсутствует, и, следовательно, что должно подлежат возврату: часы с 31.01.2020 с 22 по 30.01.2020 23. Категория B должна возвращать часы с 01.01.2020 с 0 по 31.01.2020 час 1.

Ответы [ 3 ]

1 голос
/ 18 июня 2020

Вам нужно несколько шагов. Во-первых, для каждой записи вам нужно увидеть, сколько часов непрерывных предшествующих данных у нее есть. Это то, что делает предложение grouped_hour_data в приведенном ниже решении.

Затем вам нужно выбрать из этого результата, получая только строки, которые имеют полные 24 часа непрерывных предшествующих данных. Затем выберите только первые 24 строки из этого.

Это решение упрощено, чтобы воспользоваться тем фактом, что все ваши даты были усечены до часа и не было дубликатов. Если ваша проблема более сложная, это решение все еще может поддерживать ее, но ее необходимо будет пересмотреть.

В этом примере мы создаем тестовые данные за несколько дней, но удаляем данные за отдельные часы на 16-е и 17-е, так что 1-й непрерывный 24-часовой период заканчивается раньше 16-го числа.

alter session set nls_date_format = 'DD-MON-YYYY HH24:MI:SS';

with hour_data_raw AS (
SELECT to_date('17-JUN-2020 17:00:00','DD-MON-YYYY HH24:MI:SS') - ( INTERVAL '1' HOUR ) * rownum dte
FROM dual
CONNECT BY rownum <= 200 ),
hour_data AS ( SELECT dte 
               FROM hour_data_raw
               WHERE NOT TRUNC(dte,'HH') = to_date('17-JUN-2020 02:00:00','DD-MON-YYYY HH24:MI:SS')
               AND NOT TRUNC(dte,'HH') = to_date('16-JUN-2020 02:00:00','DD-MON-YYYY HH24:MI:SS') ),
-- SOLUTION BEGINS HERE... everything above is just test data
-- WITH...
   grouped_hour_data AS (
     SELECT h.*, count(trunc(h.dte,'HH')) OVER ( ORDER BY dte desc RANGE BETWEEN CURRENT ROW AND INTERVAL '1' DAY - INTERVAL '1' SECOND FOLLOWING ) cnt
     FROM hour_data h
     ORDER BY dte)
SELECT * FROM grouped_hour_data
WHERE cnt = 24
ORDER BY dte desc
FETCH FIRST 24 ROWS ONLY;
+----------------------+-----+
|         DTE          | CNT |
+----------------------+-----+
| 16-JUN-2020 01:00:00 |  24 |
| 16-JUN-2020 00:00:00 |  24 |
| 15-JUN-2020 23:00:00 |  24 |
| 15-JUN-2020 22:00:00 |  24 |
| 15-JUN-2020 21:00:00 |  24 |
| 15-JUN-2020 20:00:00 |  24 |
| 15-JUN-2020 19:00:00 |  24 |
| 15-JUN-2020 18:00:00 |  24 |
| 15-JUN-2020 17:00:00 |  24 |
| 15-JUN-2020 16:00:00 |  24 |
| 15-JUN-2020 15:00:00 |  24 |
| 15-JUN-2020 14:00:00 |  24 |
| 15-JUN-2020 13:00:00 |  24 |
| 15-JUN-2020 12:00:00 |  24 |
| 15-JUN-2020 11:00:00 |  24 |
| 15-JUN-2020 10:00:00 |  24 |
| 15-JUN-2020 09:00:00 |  24 |
| 15-JUN-2020 08:00:00 |  24 |
| 15-JUN-2020 07:00:00 |  24 |
| 15-JUN-2020 06:00:00 |  24 |
| 15-JUN-2020 05:00:00 |  24 |
| 15-JUN-2020 04:00:00 |  24 |
| 15-JUN-2020 03:00:00 |  24 |
| 15-JUN-2020 02:00:00 |  24 |
+----------------------+-----+

EDIT: обработка поля категории

Для обработки дополнительное поле category, которое вы добавили, вам нужно сделать несколько вещей.

Во-первых, PARTITION BY category при вычислении поля cnt. Это приведет к тому, что данные каждой категории будут обрабатываться отдельно при вычислении этого значения. Так, например, значение часа 2 для категории A не будет считаться значением часа 2 для категории B.

Во-вторых, вы больше не можете использовать FETCH FIRST 24 ROWS ONLY для получения желаемых данных, потому что вам нужны первые 24 строки в каждой категории. Итак, вам нужен дополнительный шаг (ordered_groups, в пересмотренном ниже запросе), чтобы упорядочить строки в каждой категории, которым предшествуют 24 часа непрерывных данных. Назовите этот заказ rn, а затем в последнем запросе просто выберите where rn <= 24.

WITH grouped_hour_data AS (
     SELECT h.*, count(trunc(h.dte,'HH')) OVER ( 
              PARTITION BY category 
              ORDER BY dte desc 
              RANGE BETWEEN CURRENT ROW 
                  AND INTERVAL '1' DAY - INTERVAL '1' SECOND FOLLOWING ) cnt
     FROM hour_data h
     ORDER BY dte),
 ordered_groups AS (
   SELECT ghd.*, row_number() over ( partition by ghd.category order by ghd.dte desc ) rn
   FROM   grouped_hour_data
   WHERE  ghd.cnt = 24 )
SELECT * FROM ordered_groups
WHERE rn <= 24;
ORDER BY category, dte desc;

Раскрытие информации: я не тестировал этот обновленный logi c, поэтому могут быть некоторые ошибки.

0 голосов
/ 18 июня 2020

[РЕДАКТИРОВАТЬ] Для вас должно работать следующее:

IF OBJECT_ID('tempdb..#hours') IS NOT NULL 
    DROP TABLE #hours

create table #hours ([Hour] int)

insert into #hours select 1
insert into #hours select 2
insert into #hours select 3
insert into #hours select 4
insert into #hours select 5
insert into #hours select 6
insert into #hours select 7
insert into #hours select 8
insert into #hours select 9
insert into #hours select 10
insert into #hours select 11
insert into #hours select 12
insert into #hours select 13
insert into #hours select 14
insert into #hours select 15
insert into #hours select 16
insert into #hours select 17
insert into #hours select 18
insert into #hours select 19
insert into #hours select 20
insert into #hours select 21
insert into #hours select 22
insert into #hours select 23
insert into #hours select 24

-- step 1 --
IF OBJECT_ID('tempdb..#temp1') IS NOT NULL 
    DROP TABLE #temp1

select
    t.[Date]
    ,convert(date,t.[Date]) [Day]
    ,datepart(hour,t.[Date]) [Hour]
    ,t.Value

into
    #temp1

from
    #yourtable t

-- step 2 --
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL 
    DROP TABLE #temp2

select
    max(t.[Day]) [MostRecentDay]

into
    #temp2

from
    #temp1 t
    cross apply (
        select
            count(distinct i.[Hour]) [HrCt]
        from
            #temp1 i
        where
            t.[Day] = i.[Day]
    ) hc

where
    hc.HrCt <> 24

-- step 3 --
IF OBJECT_ID('tempdb..#temp3') IS NOT NULL 
    DROP TABLE #temp3

select
    min(t1.[Hour]) [FirstBlank]

into
    #temp3

from
    #temp2 t2
    inner join #temp1 t1
        on t2.[MostRecentDay] = t1.[Day]
    left outer join #hours h
        on t1.[Hour] = h.[Hour]

where
    h.[Hour] is null

-- final select --
select top 24
    t1.[Date]
    ,t1.[Value]

from
    #temp1 t1
    cross join #temp2 t2
    cross join #temp3 t3

where
    t1.[Date] < convert(datetime,concat(t2.[MostRecentDay],' ',t3.[FirstBlank]))

order by
    t1.[Date] desc
0 голосов
/ 17 июня 2020

Похоже, вы действительно просматриваете последние 24 строки в своей почасовой таблице. Если это так, вы можете использовать предложение, ограничивающее количество строк:

select date, value
from mytable
order by date desc
fetch first 24 rows only

Или, если может быть несколько записей в час, тогда другой вариант: dense_rank():

select date, value
from (
    select t.*, dense_rank() over(order by trunc(date, 'hh24') desc) rn
    from mytable t
) t
where rn <= 24
...