Создание журнала состояния из строк даты и времени изменений состояния - PullRequest
0 голосов
/ 13 марта 2019

Я собираю некоторые данные из удаленного API в локальную таблицу SQL Server, которая отформатирована следующим образом.(представьте, что он отсортирован по убыванию StatusDT)

DriverID     StatusDT                 Status
--------     --------                 ------
b103         2019-03-05 05:42:52:000  D
b103         2019-03-03 23:45:42.000  SB
b103         2019-03-03 21:49:41.000  ON

Каков наилучший способ в конечном итоге добраться до точки, в которой я могу вернуть запрос, показывающий общее количество времени, потраченного в каждом статусе на каждый день для каждогодрайвер?

Кроме того, возможно, что между обновлениями статуса могут быть промежутки в целый день или более, и в этом случае мне понадобится строка, показывающая продолжение предыдущего статуса с 00:00:00 до23:59:59 за каждый пропущенный день.Итак, если я перебираю эту таблицу, чтобы заполнить другую с приведенной ниже структурой, приведенный выше пример должен будет выглядеть примерно так ... (опять же, отсортировано по убыванию по дате)

DriverID  StartDT              EndDT               Status
--------  ---------------      --------------      ------
b103      2019-03-05 05:42:52                      D
b103      2019-03-05 00:00:00  2019-03-05 05:42:51 SB
b103      2019-03-04 00:00:00  2019-03-04 23:59:59 SB
b103      2019-03-03 23:45:42  2019-03-03 23:59:59 SB
b103      2019-03-03 21:49:41  2019-03-03 23:45:41 ON

что имеет смысл?

Я свалил данные API в «рабочую» таблицу и надел на нее курсор, чтобы добавить строки в другую таблицу с начальной и конечной датой / временем, но мне любопытноесли есть другой способ, который может быть более эффективным.

Большое спасибо.

Ответы [ 2 ]

0 голосов
/ 14 марта 2019

Большая часть вашего ответа просто использует lead():

select driverid, status, statusdt,
       lead(statusdt) over (partition by driverid order by statusdt) as enddte
from t;

Это не дает перерывов на день. Но вы можете добавить их. Я думаю, что самый простой способ - это добавить даты (используя рекурсивный CTE) и вычислить статус в это время. Итак:

Я бы сделал следующее:

  • использовать рекурсивный CTE для вычисления дат
  • "заполнить" статусы и объединение в исходную таблицу
  • используйте lead(), чтобы получить дату окончания

Это выглядит так:

with day_boundaries as (
      select driverid, dateadd(day, 1, convert(min(statusdt) as date) as statusdt, max(statusdt) as finaldt
      from t 
      group by driverid
      having datediff(da, min(statusdt), max(statusdt)) > 0
      union all
      select driverid, dateadd(day, 1, statusdt), finaldt
      from day_boundaries
      where statusdt < finaldt
     ),
     unioned as (
      select driverid, status, statusdt
      from t
      union all
      select db.driverid, s.status, db.statusdt
      from day_boundaries db cross apply
           (select top (1) status
            from t
            where t.statusdt < db.statusdt
            order by t.statusdt desc
           ) s
     )
select driverid, status, statusdt,
           lead(statusdt) over (partition by driverid order by statusdt) as enddte
from unioned;

Обратите внимание, что это не вычитает ни одной секунды из даты окончания. Дата окончания соответствует предыдущей дате начала. Время непрерывно. Нет смысла иметь пробелы для записей, которые должны плотно прилегать друг к другу.

0 голосов
/ 13 марта 2019

Я думаю, что этот запрос - то, что вам нужно.Однако я не смог проверить его на синтаксические ошибки:

with x as (
  select
    DriverID,
    StatusDT as StartDT,
    lead(StatusID) over(partition by DriverID order by StatusDT) as EndDT,
    Status
  from my_table
)
select -- start & end on the same day
  DriverID,
  StartDT,
  EndDT,
  Status
from x
where convert(date, StartDT) = convert(date, EndDT) 
   or EndDT is null
union all
select -- start & end on different days; first day up to midnight
  DriverID,
  StartDT,
  dateadd(ms, -3, convert(date, EndDT)) as EndDT,
  Status
from x
where convert(date, StartDT) <> convert(date, EndDT)
  and or EndDT is not null
union all
select -- start & end on different days; next day from midnight
  DriverID,
  convert(date, EndDT) as StartDT,
  EndDT,
  Status
from x
where convert(date, StartDT) <> convert(date, EndDT)
  and or EndDT is not null
order by StartDT desc
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...