Reputation: 345
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.
Upvotes: 1
Views: 1708
Reputation: 43626
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
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]
and finally sumarize the minutes for each badge_no:
SELECT [badge_no]
,SUM([Minutes])
FROM TimesPerBadge_No
GROUP BY [badge_no]
Upvotes: 1
Reputation: 21887
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')
Upvotes: 2