这里有一个选项供您使用一些 CTE 和 row_numbers。基本上,它为每个用户对事件进行排序,然后查找在注销后或不执行任何操作的登录列表,然后查找执行登录或不执行任何操作的注销列表,然后将它们关联成对。
;with events as (
select *,
row_number() over(partition by Requestor order by TimeOfAction) row
from EventLog
), logins as (
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
from events e1
left join events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row+1
where e1.Activity='Login'
and e1.Activity!=isnull(e2.Activity, 'Logout')
), logouts as (
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
from events e1
left join events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row-1
where e1.Activity='Logout'
and e1.Activity!=isnull(e2.Activity, 'Login')
)
select i.Requestor, i.TimeOfAction as loginTime, o.TimeOfAction as logoutTime
from logins i
left join logouts o on i.Requestor=o.Requestor
and i.row=o.row
注意:通过将部分或全部 CTE 查询拆分到临时表中,可以(大幅?)提高查询性能。即类似于以下内容:
select *,
row_number() over(partition by Requestor order by TimeOfAction) row
into #events
from EventLog
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
into #logins
from #events e1
left join #events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row+1
where e1.Activity='Login'
and e1.Activity!=isnull(e2.Activity, 'Logout')
select e1.Activity, e1.TimeOfAction, e1.Requestor,
row_number() over(partition by e1.Requestor order by e1.TimeOfAction) row
into #logouts
from #events e1
left join #events e2 on e1.Requestor=e2.Requestor
and e1.row=e2.row-1
where e1.Activity='Logout'
and e1.Activity!=isnull(e2.Activity, 'Login')
select i.Requestor, i.TimeOfAction as loginTime, o.TimeOfAction as logoutTime
from #logins i
left join #logouts o on i.Requestor=o.Requestor
and i.row=o.row
drop table #logouts
drop table #logins
drop table #events