Мой подход к этой проблеме - выяснить, каким было последнее событие windows
, и отфильтровать его. Если последнее событие было on
, то мы должны записать его;в противном случае мы не делаем. В качестве бонуса это означает, что вам может не потребоваться предыдущая предварительная обработка - я не проверял все крайние случаи, но если ваши данные начинаются с выключенного значения windows
, то вы должны быть хороши (так что вы можете просто объединиться сфиктивная строка)
Обратите внимание, что для удобства я внес небольшие изменения в типы данных - я преобразовал столбцы даты и времени в один столбец datetime и превратил OnOff в битовый столбец. Оглядываясь назад, возможно, вторая корректировка не была необходима. Получение первой настройки должно быть достаточно простым;Вы могли бы сделать это как часть ваших CTE предварительной обработки.
Я поместил ваши данные во временную таблицу, поэтому мне было легче экспериментировать;вы, вероятно, захотите заменить каждый экземпляр #test
именем вашего CTE.
Скрипка SQL / определение данных:
create table #test(
id int primary key identity,
PCNum int,
Code varchar(50),
OnOff bit,
dt datetime
)
;
insert into #test(PCNum, Code, OnOff, dt)
values
(5258, 'windows' , 1, '10/11/2019 10:00:20'),
(5258, 'steam' , 1, '10/11/2019 10:00:55'),
(5258, 'origin' , 1, '10/11/2019 10:01:03'),
(5258, 'origin' , 0, '10/11/2019 10:10:04'),
(5258, 'steam' , 0, '10/11/2019 10:12:45'),
(5258, 'windows' , 0, '10/11/2019 10:13:06'),
(5258, 'bios' , 0, '10/11/2019 10:14:01'),
(5258, 'power' , 0, '10/11/2019 10:14:22'),
(5258, 'power' , 1, '10/11/2019 11:34:45'),
(5258, 'bios' , 1, '10/11/2019 11:34:56'),
(5258, 'windows' , 1, '10/11/2019 11:35:03'),
(5258, 'skype' , 1, '10/11/2019 11:35:06'),
(5258, 'skype' , 0, '10/11/2019 11:56:52'),
(5258, 'windows' , 0, '10/11/2019 11:57:07')
;
Фактический запрос:
with last_windows(id, dt) as
(
select t1.id, max(t2.dt) as dt from #test as t1
cross join #test as t2
where datediff(second, t1.dt, t2.dt) <= 0
and t2.code = 'windows'
group by t1.id
),
windows_active(id, OnOff) as
(
select w.id, t.OnOff
from #test as t
left join last_windows as w on w.dt = t.dt
where t.Code = 'windows'
)
select t.id, t.PCNum, t.Code, t.OnOff, t.dt
from #test as t
left join windows_active as wa on wa.id = t.id
where wa.OnOff = 1
or t.Code = 'windows'
;
Объяснение: CTE last_windows
отвечает за определение времени последней windows
активности для каждой строки (ID). Это происходит путем перекрестного соединения стола против самого себя. Первый экземпляр называется t1
, а второй t2
. t2
фильтруется так, чтобы включать только строки windows
, а также включать события только в t1
или раньше. Если затем мы берем максимальное t2
datetime для каждого идентификатора в t1
, это представляет время последнего windows
действия.
Из-за работы агрегатов мы не можем на самом деле добавитьОтключить данные до last_windows
. windows_active
обеспечивает удобство - он соединяет #test
с last_windows
, чтобы эффективно добавлять данные OnOff к last_windows
. Нам остается таблица, которая сообщает нам, какой была последняя активность windows
для каждой строки в #test
.
Остальная часть запроса тривиальна - запрос объединяет windows_active
в #test
и фильтрует только для тех случаев, когда было включено последнее событие windows
. Это также отфильтровывает все случаи, когда окна отключаются, поэтому есть оператор or
, включающий все windows
события.
Обратите внимание, что, к сожалению, из-за перекрестного соединения этот запрос может быть медленным на больших данныхнаборы.