Это было отличное и приятное упражнение.
Итак, уловка заключается в следующем:
- Человек снова вошел в систему до выхода из своего последнего сеанса и завершил сеанс после своего первого сеанса;или
- Человек снова вошел в систему, прежде чем выйти из своего последнего сеанса, и завершил сеанс, прежде чем завершить свой первый сеанс.
Итак, для решения этой проблемы достаточно просто назначить новыйвремя начала сеанса время, равное времени окончания его предыдущего сеанса.В этом случае у вас будет каждый сеанс в виде строки, и вы сможете рассчитать разницу во времени.Хорошо, давайте смоделируем примеры этого:
create table #temp (userId int, timeComienza datetime, timeTermina dateTime )
-- exemplo de overlap
insert into #temp values (1, '20180102 16:00', '20180102 16:30')
insert into #temp values (1, '20180102 16:25', '20180102 16:35')
insert into #temp values (1, '20180102 16:55', '20180102 17:15')
-- ejemplo de no overlap
insert into #temp values (2, '20180102 16:00', '20180102 16:30')
insert into #temp values (2, '20180102 16:35', '20180102 16:50')
insert into #temp values (2, '20180102 16:40', '20180102 16:45')
userId timeComienza timeTermina
1 2018-01-02 16:00:00.000 2018-01-02 16:30:00.000
1 2018-01-02 16:25:00.000 2018-01-02 16:35:00.000
1 2018-01-02 16:55:00.000 2018-01-02 17:15:00.000
2 2018-01-02 16:00:00.000 2018-01-02 16:30:00.000
2 2018-01-02 16:35:00.000 2018-01-02 16:50:00.000
2 2018-01-02 16:40:00.000 2018-01-02 16:45:00.000
Как вы можете видеть, пользователь 1 страдает от входа в свой второй сеанс перед завершением своего первого сеанса, а пользователь 2 страдает от почти такой же проблемы в своем третьем сеансе, за исключением того, чтоего третья сессия закончилась до окончания его 2-й сессии (полное наложение и затмение его второй сессии).
Первое, что нам нужно сделать, - это обеспечить порядок этой сессии с использованием order by.
select *, ROW_NUMBER() over(partition by userId order by timeComienza) as unOrden
into #temp2
from #temp
userId timeComienza timeTermina unOrden
1 2018-01-02 16:00:00.000 2018-01-02 16:30:00.000 1
1 2018-01-02 16:25:00.000 2018-01-02 16:35:00.000 2
1 2018-01-02 16:55:00.000 2018-01-02 17:15:00.000 3
2 2018-01-02 16:00:00.000 2018-01-02 16:30:00.000 1
2 2018-01-02 16:35:00.000 2018-01-02 16:50:00.000 2
2 2018-01-02 16:40:00.000 2018-01-02 16:45:00.000 3
Теперь было бы в 100 раз легче работать с нашими итерациями.Давайте создадим пустую таблицу с той же структурой, что и таблица №2, которая будет нашей таблицей для вставки нашего анализа.
select *
into #tablaInsertar
from #temp2
delete from #tablaInsertar
И, наконец, вот ядро нашего анализа:)
-- variable to iterate users
declare @x int = 1 , @usuarios int = 1, @usuariosMax int
--num dif de usuarios:
select @usuariosMax = count(distinct(userId)) from #temp2
while(@usuarios <= @usuariosMax)
begin
/*trabajando cada usuario*/
/*Primero necesitamos saber la longitud de cada Usuario*/
declare @trabajaUsuario int = 1, @longUsuario int
--obtiene longitud usuario
select @longUsuario = count(1) from #temp
where userId = @usuarios
while(@trabajaUsuario <= @longUsuario)
begin
if(@trabajaUsuario = 1)
begin
insert into #tablaInsertar
select
*
from #temp2
where userId = @usuarios and unOrden = @trabajaUsuario
end
else -- dado que no sea la primera fila
-- comparando horas
begin
declare @horaInicioEstePeriodo dateTime, @horaTerminaAnterior dateTime
select @horaInicioEstePeriodo = #temp2.timeComienza from #temp2 where userId = @usuarios and unOrden = @trabajaUsuario
select @horaTerminaAnterior = #temp2.timeTermina from #temp2 where userId = @usuarios and unOrden = @trabajaUsuario - 1
if(@horaInicioEstePeriodo < @horaTerminaAnterior) -- las modificaciones dado que el periodo inicio sea menro a la hora anterior
begin
insert into #tablaInsertar
select
t2.userId
, t1.timeTermina as tiempoComienzaActualizado
, t2.timeTermina
, t2.unOrden
from
(
select
#temp2.userId
,#temp2.timeComienza
, #temp2.timeTermina
, #temp2.unOrden
from #temp2
where userId = @usuarios and unOrden = @trabajaUsuario - 1
)t1
join
(
select
#temp2.userId
--, as tiempoComienzaActualizado --#temp2.timeComienza
, #temp2.timeTermina
, #temp2.unOrden
from #temp2
where userId = @usuarios and unOrden = @trabajaUsuario
) t2 on t1.userId = t2.userId and t1.unOrden + 1 = t2.unOrden
end
else -- dado que el periodo inicia sea mayor o igual a la hora anterior
begin
insert into #tablaInsertar
select
*
from #temp2
where userId = @usuarios and unOrden = @trabajaUsuario
end
end
select @trabajaUsuario += 1
end
select @usuarios += 1
end
Давайте посмотрим на нашу новую таблицу:)
select *, DATEDIFF(s,timeComienza,timeTermina) timeInSeconds
from #tablaInsertar
userId timeComienza timeTermina unOrden timeInSeconds
1 2018-01-02 16:00:00.000 2018-01-02 16:30:00.000 1 1800
1 2018-01-02 16:30:00.000 2018-01-02 16:35:00.000 2 300
1 2018-01-02 16:55:00.000 2018-01-02 17:15:00.000 3 1200
2 2018-01-02 16:00:00.000 2018-01-02 16:30:00.000 1 1800
2 2018-01-02 16:35:00.000 2018-01-02 16:50:00.000 2 900
2 2018-01-02 16:50:00.000 2018-01-02 16:45:00.000 3 -300
Как мы видим, сессия 2 пользователя 1 теперь правильно отражает его простые 300 секунд, которые он действительно работал (5 минут).И для проблемы сеанса 3 пользователя 2 у нас есть отрицательное число, и причина в том, что он берет время из своего сеанса 2. Итак, все, что нам нужно сделать, это суммировать положительные значения только для того, чтобы узнать реальное время, записанное каждым пользователем,вот так:
select
t1.userId,
sum(case when timeInSeconds > 0 then timeInSeconds else 0 end) totalTimeLogged
from
(
select *, DATEDIFF(s,timeComienza,timeTermina) timeInSeconds
from #tablaInsertar
) t1
group by t1.userId
Окончательный результат:
userId totalTimeLogged
1 3300
2 2700