Question

I am trying to run a query against a 3rd party employee time tracking database. As far as I can tell, they do not keep a running total on an employees time throughout the day. What I have are a series of rows containing a user ID and a time stamp. If I order the rows by the time stamp, I effectively get their history of punches. If I assume that punch 1 is in, punch 2 is out, punch 3 is in, etc etc, is there an effective method of finding the DATEDIFF in minutes from every other row and then SUM them up to get the total time for the day for that employee?

    badge_no punch_timestamp
    11209   1/31/14 7:58 AM
    11209   1/31/14 9:57 AM
    11209   1/31/14 10:00 AM
    11209   1/31/14 10:07 AM

Not 2 minutes after I posted I came across this post: SQL Server find datediff between different rows, sum

I will give that a try first.

Was it helpful?

Solution 2

Using @DaveZych sample data I have managed to calculated the same results as him, using the SQL statement below:

;WITH DataSource ([StartOrEnd], [badge_no], [punch_timestamp]) AS
(
    SELECT ROW_NUMBER() OVER (PARTITION BY [badge_no] ORDER BY [punch_timestamp]) +
           ROW_NUMBER() OVER (PARTITION BY [badge_no] ORDER BY [punch_timestamp])  % 2
          ,[badge_no]
          ,[punch_timestamp]
    FROM #Time
),
TimesPerBadge_No ([badge_no], [StartOrEnd], [Minutes]) AS
(
    SELECT  [badge_no]
           ,[StartOrEnd] 
           ,DATEDIFF(MINUTE, MIN([punch_timestamp]), MAX([punch_timestamp]))
    FROM DataSource
    GROUP BY [badge_no]
            ,[StartOrEnd] 
)
SELECT [badge_no]
      ,SUM([Minutes])
FROM TimesPerBadge_No
GROUP BY [badge_no]

Here can see the values of each CTE:

First, we ned to group each start and end date:

 SELECT ROW_NUMBER() OVER (PARTITION BY [badge_no] ORDER BY [punch_timestamp]) +
           ROW_NUMBER() OVER (PARTITION BY [badge_no] ORDER BY [punch_timestamp])  % 2
          ,[badge_no]
          ,[punch_timestamp]
    FROM #Time

enter image description here

Now, we can calculate the minutes difference in each group:

SELECT  [badge_no]
        ,[StartOrEnd] 
        ,DATEDIFF(MINUTE, MIN([punch_timestamp]), MAX([punch_timestamp]))
FROM DataSource
GROUP BY [badge_no]
        ,[StartOrEnd] 

enter image description here

and finally sumarize the minutes for each badge_no:

SELECT [badge_no]
      ,SUM([Minutes])
FROM TimesPerBadge_No
GROUP BY [badge_no]

enter image description here

OTHER TIPS

Here's a relatively simple, naive way of doing it. Assuming that, like you said, each "odd" row is a stamp in and each "even" row is a stamp out, you can grab the odd and even rows separately and calculate each working block. Note that the DateDiff I used is in minutes (mi) but you can change that to hours/seconds/whatever: http://technet.microsoft.com/en-us/library/ms189794.aspx

;WITH StartTime AS
(
SELECT
    badge_no,
    punch_timestamp,
    myrow
FROM
(
    SELECT
        badge_no,
        punch_timestamp,
        ROW_NUMBER() OVER (Partition BY badge_no ORDER BY punch_timestamp ASC) as myrow
    FROM #Time
) [t1]
WHERE myrow % 2 = 1 --odd rows
)
,EndTime AS
(
SELECT
    badge_no,
    punch_timestamp,
    myrow - 1 as 'myrow' --Subtract 1 to match up with the odd rows
FROM
(
    SELECT
        badge_no,
        punch_timestamp,
        ROW_NUMBER() OVER (Partition BY badge_no ORDER BY punch_timestamp ASC) as myrow
    FROM #Time
) [t1]
WHERE myrow % 2 = 0 --even rows
)

SELECT
    badge_no,
    SUM(diff) as 'MinutesWorked'
FROM 
(
SELECT
    EndTime.badge_no,
    DATEDIFF(mi, 
             (SELECT TOP 1 
                  punch_timestamp 
              FROM StartTime 
              WHERE StartTime.badge_no = EndTime.badge_no 
                  AND StartTime.myrow = EndTime.myrow), 
             EndTime.punch_timestamp) as 'diff'
FROM EndTime
) [t1]
GROUP BY badge_no

Here is the test data I used:

CREATE TABLE #Time
(
    badge_no nvarchar(10),
    punch_timestamp datetime
)

INSERT INTO #Time VALUES ('100', '2013-01-02 12:01 PM')
INSERT INTO #Time VALUES ('100', '2013-01-02 1:38 PM')
INSERT INTO #Time VALUES ('100', '2013-01-02 2:29 PM')
INSERT INTO #Time VALUES ('100', '2013-01-03 3:01 PM')
INSERT INTO #Time VALUES ('100', '2013-01-03 4:20 PM')
INSERT INTO #Time VALUES ('100', '2013-01-04 12:01 PM')
INSERT INTO #Time VALUES ('100', '2013-01-04 2:01 PM')
INSERT INTO #Time VALUES ('100', '2013-01-04 3:11 PM')
INSERT INTO #Time VALUES ('100', '2013-01-04 4:21 PM')
INSERT INTO #Time VALUES ('100', '2013-01-05 12:01 PM')
INSERT INTO #Time VALUES ('100', '2013-01-05 1:01 PM')
INSERT INTO #Time VALUES ('200', '2013-01-04 2:11 AM')
INSERT INTO #Time VALUES ('200', '2013-01-04 4:34 PM')
INSERT INTO #Time VALUES ('200', '2013-01-05 1:01 AM')
INSERT INTO #Time VALUES ('200', '2013-01-05 4:29 AM')
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top