tags:

views:

128

answers:

3

Data:

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 

Here is the query I am currently using.

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

The problem is on the multiple IN_1 entries. I would only like to track the time difference between the first IN_1 Entry, and the Following OUT_1 Entry and Ignore the IN_1 entry in between. Of course you could have 100 IN_1 but the time is only tracked from the first IN_1 to the next OUT_1.

To complicate things further there could be an IN_1, IN_2, IN_3, OUT_1, OUT_2, OUT_3 and you could enter IN_1 and Leave OUT_3 and and it would work just as it was IN_1, OUT_1.

+1  A: 

Nick, the different doors are not an issue instead of using = 'IN_1' and = 'OUT_1' use like 'IN%' and like 'OUT%'

Hogan
I agree.. I you need to find intervals between :IN_1 <-> OUT_3 you simply cannot do it like it is configured now. Consider also adding an extra column to signal if the row-entry is an IN or an OUT event. This way you will only need to find the next OUT row to find out the exit time, regardless of the door used.
Radu094
+1  A: 

Solved

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]

Gives these results:

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

This will work for all types if IN_ and OUT_

Hogan
What about the case where the card is swiped twice on exit?
Joel Potter
change g.[timeout] as endtime to max(g.[timeout]) as endtime and take it out of the group by statement.
Hogan
hmmm... that won't work. one sec.
Hogan
Don't have time to do it now, the idea is this, use a sub-query to get next out (as above), then a sub-query to get the next in, then find the max (in terms of time) of the outs between those and you have your duration.Looks like it might take 3 sub-queries to me.My guess is iteration will be faster.
Hogan
Well I am fine with taking the 1st scan on the timeout, if they bumble and scan twice on the way out, they don't get extra time. The only thing this is to counter is somone scanning more than once on the way in. The problem i had on my query is when you would add time up it had a whole new set of time. So as long as I Get the Frist "IN" scan and the time difference of the First "OUT" scan I am good. So far with my testing this works just like it should. I will keep testing to confirm. Thanks!
Nick
good! If you don't need to worry about Joel's point then the query above should be perfect. Good luck.
Hogan
It is thanks! No I am going to play around and try to figure out duration from specific times.
Nick
A: 

CTEs will work on SQL server 2005, 2008. The test data insert is 2008 specific.

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
Damir Sudarevic