'I have a SQL Table with employees and a timestamp for their every action. How to calculate how much they worked, and how much they idled (20min gaps)?
I have a very simple query that looks like this
SELECT
Username
,FORMAT(Timestamp, 'MM/dd/yyyy') as Date
,FORMAT(Timestamp, 'hh:mm tt') as Time
FROM
AuditRecords
ORDER BY
date desc
,Username
,Timestamp
Here is a sample output of running that query.
Username | Date | Time |
---|---|---|
Employee1 | 05/10/2022 | 12:10 AM |
Employee1 | 05/10/2022 | 12:23 AM |
Employee1 | 05/10/2022 | 12:25 AM |
Employee1 | 05/10/2022 | 12:27 AM |
Employee1 | 05/10/2022 | 12:28 AM |
Employee1 | 05/10/2022 | 12:31 AM |
Employee1 | 05/10/2022 | 12:33 AM |
Employee1 | 05/10/2022 | 01:43 AM |
Employee1 | 05/10/2022 | 02:18 AM |
Employee1 | 05/10/2022 | 03:06 AM |
Employee1 | 05/10/2022 | 03:20 AM |
Employee1 | 05/10/2022 | 03:26 AM |
Employee1 | 05/10/2022 | 04:02 AM |
Employee1 | 05/10/2022 | 04:05 AM |
Employee1 | 05/10/2022 | 04:07 AM |
Employee1 | 05/10/2022 | 04:09 AM |
Employee1 | 05/10/2022 | 04:14 AM |
Employee2 | 05/10/2022 | 01:08 AM |
Employee2 | 05/10/2022 | 01:13 AM |
Employee2 | 05/10/2022 | 01:15 AM |
Employee2 | 05/10/2022 | 01:22 AM |
Employee2 | 05/10/2022 | 01:24 AM |
Employee2 | 05/10/2022 | 01:28 AM |
Employee2 | 05/10/2022 | 01:30 AM |
Employee2 | 05/10/2022 | 01:32 AM |
Employee2 | 05/10/2022 | 01:57 AM |
Employee2 | 05/10/2022 | 01:58 AM |
Employee2 | 05/10/2022 | 04:53 AM |
Employee3 | 05/10/2022 | 12:50 AM |
Employee3 | 05/10/2022 | 12:50 AM |
Employee3 | 05/10/2022 | 12:50 AM |
Employee3 | 05/10/2022 | 03:00 AM |
Employee3 | 05/10/2022 | 03:58 AM |
Employee3 | 05/10/2022 | 04:56 AM |
Employee3 | 05/10/2022 | 05:42 AM |
Now as you can see, there are three employees. Employee1 did their first task at 12:10 AM and their last task at 04:14 AM
Originally, what I was doing was taking their first and last action, and just subtracting them to see how many hours they worked.
SELECT
Username
,FORMAT(Timestamp, 'MM/dd/yyyy') as Date
,FORMAT (min(Timestamp) , 'hh:mm tt') as FirstAction
,FORMAT (max(Timestamp) , 'hh:mm tt') as LastAction
,DATEDIFF(hour, min(Timestamp),max(Timestamp)) AS HoursWorked
FROM
AuditRecords
GROUP BY
Username
,FORMAT(Timestamp, 'MM/dd/yyyy')
ORDER BY
date desc
,Username
So for the case of Employee1, it would return 4 hours. As the difference between 12AM and 4 AM.
However, now we want to calculate this metric in a more sophisticated way. For example, around the middle of his shift, his actions took place at
01:43 AM, 02:18 AM, 03:06 AM
Which is roughly 40 minutes between each action. So instead of counting this as working time, I actually want to count this as idle time.
In the beginning of Employee1's shift, he was doing actions every 2-3 minutes. So I want to count this as working time.
However, starting at 12:33 AM to 3:06 AM, since his actions are so spread apart, I want to count this as idle time.
I want to be able to set a custom idle metric. Like 30 or 40 minutes. And if their consecutive actions have more than that metric as a gap, then we count it as being idle.
Is this possible to do with a SQL query? I would have to somehow check each row compared to it's previous one.
So in the end, I want to return a total of working minutes, and a total of idle minutes, per day per employee.
Any help or guidance in the right direction would be greatly appreciated.
Solution 1:[1]
You want the lag function. Here is a basic example with it... from here you can put a simple case statement (or iif since its T-SQL) on the lower query (t1) to see if you want to count it as an idle time event or not and then just sum that up in the upper query. I should also mention that with lag function you are likely to have a null record at each grouping (in this case, its per day per employee) so you will probably want to omit those... I didn't in my example so you can have the option. If you comment out the upper query (t2) you can run just t1 alone and see what it is doing.
select
*
, totalhours-hoursworked as IdleTime
from(
select
username
,dateval
, sum(isnull(EntryMinutes,0)) / 60 as HoursWorked
, cast(datediff(second, min(timeval), max(timeval)) / 60 / 60 as decimal(38,20)) as TotalHours
from(
select
*
, EntryMinutes = cast(datediff( second,
timeval, lag(timeval, 1) over(partition by username, dateval order by username, dateval desc, timeval desc)) / 60 as decimal(38,20))
from AuditRecords
) as t1
group by username, dateval
) as t2
this should get you started
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | systematical |