Объединить диапазоны дат для строк с одинаковым значением

#sql #sql-server

#sql #sql-сервер

Вопрос:

Данные выглядят следующим образом — мой желаемый результат заключается в том, что когда следующая запись в день имеет то же значение, эти времена объединяются, чтобы получить общее время. Как только значение изменяется, преобразуйте дату первой записи (этого значения) в новое значение, чтобы мы получили объединенное время, в течение которого сохранялось это значение.

     Value            Date
60                  1/5/2019 8:00
60                  1/5/2019 9:00
60                  1/5/2019 10:00
75                  1/5/2019 10:30
60                  1/5/2019  11:00
40                  1/5/2019  12:00
40                  1/5/2019  13:00
  

Желаемый результат

     Value            Total Time
60                  1/5/2019 8:00 - 10:30 = 2 and a half hours
75                  1/5/2019 10:30 - 11:00 = half hour
60                  1/5/2019  11:00 - 12:00 = 1 hour
40                  1/5/2019  12:00 - 13:00 = 1 hour
  

Комментарии:

1. Результатом всегда будет целое число или целое с половиной?

2. Результат будет всего за несколько минут

Ответ №1:

Это проблема пробелов и островов. Для этой версии, я думаю, разница в номерах строк является самым простым решением. Итак, это почти решает вашу проблему:

 select value, min(date), max(date)
from (select t.*,
             row_number() over (order by date) as seqnum,
             row_number() over (partition by value order by date) as seqnum_v
      from t
     ) t
group by (seqnum - seqnum_v), value;
  

Но вы хотите следующий старт, поэтому нам также нужен lead() :

 select value, min(date) as startdate,
       lead(min(date), 1, max(date)) over (order by min(date)) as enddate
from (select t.*,
             row_number() over (order by date) as seqnum,
             row_number() over (partition by value order by date) as seqnum_v
      from t
     ) t
group by (seqnum - seqnum_v), value;
  

И получить общее время:

 select value,
       datediff(minute,
                min(date),
                lead(min(date), 1, max(date)) over (order by min(date))
               ) as dur_minutes
from (select t.*,
             row_number() over (order by date) as seqnum,
             row_number() over (partition by value order by date) as seqnum_v
      from t
     ) t
group by (seqnum - seqnum_v), value;