Consulta de rastreamento de tempo SQL
Pergunta
Dados:
EmpNumber, TimeStamp, AreaName 10632, 2009-11-23 16:40:33.000, OUT_1 10632, 2009-11-23 16:39:03.000, IN_1 10632, 2009-11-23 16:38:56.000, IN_1 10632, 2009-11-23 15:31:51.000, OUT_1 10632, 2009-11-23 15:31:48.000, IN_1 10632, 2009-11-23 15:31:43.000, IN_1 10632, 2009-11-23 15:31:14.000, OUT_1 10632, 2009-11-23 15:31:08.000, IN_1 10632, 2009-11-23 15:29:18.000, OUT_1 10632, 2009-11-23 15:28:29.000, IN_1 10632, 2009-11-23 15:27:35.000, OUT_1 10632, 2009-11-23 15:26:35.000, IN_1 10632, 2009-11-23 15:22:55.000, IN_1
Aqui está a consulta que estou usando atualmente.
SELECT [EmpNumber], [TimeStamp], [AreaName],
DATEDIFF(second, [TimeStamp], (SELECT TOP 1 [TimeStamp]
FROM [EventTable] EV2
WHERE EV2.[TimeStamp] > EV1.[TimeStamp]
AND AreaName = 'OUT_1'
AND EV2.[EmpNumber] = EV1.[EmpNumber])
)/60.00 DurationMins
FROM [EventTable] EV1
WHERE AreaName = 'IN_1'
ORDER BY [TimeStamp] DESC
O problema está no múltiplo IN_1
entradas. Eu só gostaria de acompanhar a diferença horária entre o primeiro IN_1
Entrada e o seguinte OUT_1
Entrar e ignorar o IN_1
entrada no meio. Claro que você pode ter 100 IN_1
Mas o tempo é rastreado apenas do primeiro IN_1
para o próximo OUT_1
.
Para complicar as coisas mais adiante, pode haver um IN_1
, IN_2
, IN_3
, OUT_1
, OUT_2
, OUT_3
E você pode entrar IN_1
e sair OUT_3
e e funcionaria exatamente como era IN_1
, OUT_1
.
Solução
Resolvido
declare @test table (
ID int,
empnumber int,
timestamp datetime,
areaname varchar(20)
)
INSERT INTO @test VALUES (1, 10632, '2009-11-23 16:40:33.000', 'OUT_1' )
INSERT INTO @test VALUES (2, 10632, '2009-11-23 16:39:03.000', 'IN_1' )
INSERT INTO @test VALUES (3, 10632, '2009-11-23 16:38:56.000', 'IN_1' )
INSERT INTO @test VALUES (4, 10632, '2009-11-23 15:31:51.000', 'OUT_1' )
INSERT INTO @test VALUES (5, 10632, '2009-11-23 15:31:48.000', 'IN_1' )
INSERT INTO @test VALUES (6, 10632, '2009-11-23 15:31:43.000', 'IN_1' )
INSERT INTO @test VALUES (7, 10632, '2009-11-23 15:31:14.000', 'OUT_1' )
INSERT INTO @test VALUES (8, 10632, '2009-11-23 15:31:08.000', 'IN_1' )
INSERT INTO @test VALUES (9, 10632, '2009-11-23 15:29:18.000', 'OUT_1' )
INSERT INTO @test VALUES (10, 10632, '2009-11-23 15:28:29.000', 'IN_1' )
INSERT INTO @test VALUES (11, 10632, '2009-11-23 15:27:35.000', 'OUT_1' )
INSERT INTO @test VALUES (12, 10632, '2009-11-23 15:26:35.000', 'IN_1' )
INSERT INTO @test VALUES (13, 10632, '2009-11-23 15:22:55.000', 'IN_1' )
select g.empnumber, min(g.[timestamp]) as starttime, g.[timeout] as endtime, DATEDIFF(second,min(g.[timestamp]),g.[timeout])/60 as mins
FROM
(
select empnumber, [timestamp], (
SELECT TOP 1 s.[timestamp] FROM @test s
WHERE s.areaname like 'OUT%' AND s.[timestamp] > base.[timestamp]
ORDER BY s.[timestamp] ASC) as [timeout]
from @test base
where base.areaname like 'IN%'
) g
GROUP BY g.empnumber, g.[timeout]
Fornece estes resultados:
empnumber starttime endtime mins
10632 2009-11-23 15:22:55.000 2009-11-23 15:27:35.000 4
10632 2009-11-23 15:28:29.000 2009-11-23 15:29:18.000 0
10632 2009-11-23 15:31:08.000 2009-11-23 15:31:14.000 0
10632 2009-11-23 15:31:43.000 2009-11-23 15:31:51.000 0
10632 2009-11-23 16:38:56.000 2009-11-23 16:40:33.000 1
Isso funcionará para todos os tipos se in_ e fora_
Outras dicas
Nick, as diferentes portas não são um problema em vez de usar = 'IN_1'
e = 'OUT_1'
usar like 'IN%'
e like 'OUT%'
Os CTEs funcionarão no SQL Server 2005, 2008. A inserção de dados de teste é específica de 2008.
DECLARE @EventTable TABLE
(
EmpNumber int
,[TimeStamp] datetime
,AreaName varchar(5)
)
INSERT INTO @EventTable
( EmpNumber, [TimeStamp], AreaName )
VALUES
( 10632, '2009-11-23 16:40:33.000', 'OUT_1' )
, ( 10632, '2009-11-23 16:39:03.000', 'IN_1' )
, ( 10632, '2009-11-23 16:38:56.000', 'IN_1' )
, ( 10632, '2009-11-23 15:31:51.000', 'OUT_1' )
, ( 10632, '2009-11-23 15:31:48.000', 'IN_1' )
, ( 10632, '2009-11-23 15:31:43.000', 'IN_1' )
, ( 10632, '2009-11-23 15:31:14.000', 'OUT_1' )
, ( 10632, '2009-11-23 15:31:08.000', 'IN_1' )
, ( 10632, '2009-11-23 15:29:18.000', 'OUT_1' )
, ( 10632, '2009-11-23 15:28:29.000', 'IN_1' )
, ( 10632, '2009-11-23 15:27:35.000', 'OUT_1' )
, ( 10632, '2009-11-23 15:26:35.000', 'IN_1' )
, ( 10632, '2009-11-23 15:22:55.000', 'IN_1' )
;
WITH cte_1 -- order by time and spilt to InTime, OutTime
AS ( SELECT
EmpNumber
,case WHEN AreaName LIKE 'IN%' THEN [TimeStamp]
ELSE NULL
END AS InTime
,case WHEN AreaName LIKE 'OUT%' THEN [TimeStamp]
ELSE NULL
END AS OutTime
,AreaName
,row_number() OVER ( ORDER BY [TimeStamp] ASC ) AS rn
FROM
@EventTable
),
cte_2 -- mark those that repeat
AS ( SELECT
t.EmpNumber
,t.InTime
,t.OutTime
,t.AreaName
,t.rn
,case WHEN ( SELECT AreaName
FROM cte_1 AS x
WHERE x.rn = t.rn - 1
) = t.AreaName THEN 1
ELSE 0
END AS mrk
FROM cte_1 AS t
),
cte_3 --extract non repeats and group
AS ( SELECT
*
,row_number() OVER ( PARTITION BY AreaName ORDER BY rn ASC ) AS rn2
FROM cte_2
WHERE mrk = 0
)
SELECT
t1.EmpNumber
,t1.InTime
,t2.Outtime
,datediff(ss, t1.InTime, t2.OutTime) AS Duration
FROM
cte_3 AS t1
JOIN cte_3 AS t2 ON t1.rn2 = t2.rn2
WHERE
t1.Intime IS NOT NULL
AND t2.Outtime IS NOT NULL
ORDER BY
t1.rn