How to calculate time duration using Microsoft SQL? - sql

I want to find the time duration for each person from one start time. I want to calculate the time duration from 1 start time for each day and multiple end times for multiple users. This is my code:
SELECT *,
CAST(DATEDIFF(n, CAST(End_Time AS datetime),
CAST(Start_Time AS datetime)) AS FLOAT) / 60 AS Time_Duration
FROM
( SELECT NAME,
MAX(CASE WHEN DESCRIPTION = 'Green' THEN Final_Value END) AS Start_Time,
MAX(CASE WHEN DESCRIPTION = 'Red' THEN Final_Value END) AS End_Time
FROM mydata
WHERE NAME != ‘NA’
GROUP BY NAME
) C
I am not able to get any results for time duration.
This is what my output looks like:
Name Start_time End_time Time_Duration
1 Day_1 5/6/15 2:30
2 John 5/6/15 3:30
3 Ben 5/6/15 4:30
4 Mike 5/6/15 5:30
5 Day_2 5/7/15 2:30
6 John_2 5/7/15 4:30
7 Ben_2 5/7/15 5:30
8 Mike_2 5/7/15 6:30
I want it to look like this:
Name Start_time End_time Time_Duration
1 Day_1 5/6/15 2:30
2 John 5/6/15 3:30 1.00
3 Ben 5/6/15 4:30 2.00
4 Mike 5/6/15 5:30 3.00
5 Day_2 5/7/15 2:30
6 John_2 5/7/15 4:30 2.00
7 Ben_2 5/7/15 5:30 3.00
8 Mike_2 5/7/15 6:30 4.00

Assuming that the values in name column has suffix of the day number (and none for day 1)
WITH td AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [day] ORDER BY final_value) rnum
FROM (SELECT *,
CASE WHEN CHARINDEX('_', name) = 0
THEN '1'
ELSE SUBSTRING(name, CHARINDEX('_', name) + 1, LEN(name) - CHARINDEX('_', name))
END [day]
FROM t_dur
) tt
)
SELECT t1.name,
CASE WHEN rnum = 1 THEN t1.final_value END start_time,
CASE WHEN rnum <> 1 THEN t1.final_value END end_time,
CASE CAST(DATEDIFF(hour, (SELECT t2.final_value FROM td t2 WHERE t2.[day] = t1.[day] AND t2.rnum = 1),
t1.final_value) AS DECIMAl(5,2))
WHEN 0 THEN NULL
ELSE CAST(DATEDIFF(hour, (SELECT t2.final_value FROM td t2 WHERE t2.[day] = t1.[day] AND t2.rnum = 1),
t1.final_value) AS DECIMAl(5,2))
END time_duration
FROM td t1
Result
name start_time end_time time_duration
Day_1 2015-05-06 02:30:00.000 NULL NULL
John NULL 2015-05-06 03:30:00.000 1.00
Ben NULL 2015-05-06 04:30:00.000 2.00
Mike NULL 2015-05-06 05:30:00.000 3.00
Day_2 2015-05-07 02:30:00.000 NULL NULL
John_2 NULL 2015-05-07 04:30:00.000 2.00
Ben_2 NULL 2015-05-07 05:30:00.000 3.00
Mike_2 NULL 2015-05-07 06:30:00.000 4.00

Related

How can I create a "start" "end" time table from a timestamp list

I am trying to create a view that displays the time of employee stamps.
This is what the table looks like now:
Person
Person_Number
Date
Stamp_number
Time_Stamp
Paul
1
22-10-24
1
8:00
Paul
1
22-10-24
2
10:00
Paul
1
22-10-24
3
10:30
Paul
1
22-10-24
4
12:00
Jimmy
2
22-10-23
1
9:00
Jimmy
2
22-10-23
2
11:00
Jimmy
2
22-10-23
3
12:00
And I would like it to look like this using only a select query
Person
Person_Number
Date
Start
End
Duration
Paul
1
22-10-24
8:00
10:00
2:00
Paul
1
22-10-24
10:30
12:00
1:30
Jimmy
2
22-10-23
9:00
11:00
2:00
Jimmy
1
22-10-23
12:00
null
null
Is it possible ?
We can use conditional aggregation along with a ROW_NUMBER trick:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Person_Number, Date
ORDER BY Stamp_number) - 1 rn
FROM yourTable
)
SELECT Person, Person_Number, Date,
MAX(CASE WHEN rn % 2 = 0 THEN Time_Stamp END) AS [Start],
MAX(CASE WHEN rn % 2 = 1 THEN Time_Stamp END) AS [End],
DATEDIFF(MINUTE,
MAX(CASE WHEN rn % 2 = 0 THEN Time_Stamp END),
MAX(CASE WHEN rn % 2 = 1 THEN Time_Stamp END)) AS Duration
FROM cte
GROUP BY Person, Person_Number, Date, rn / 2
ORDER BY 2, 4;
Here is a working demo.
Try the following:
SELECT Person, Person_Number, Date, [Start], [End],
CONVERT(TIME(0), CONVERT(DATETIME, [End]) - CONVERT(DATETIME, [Start])) AS Duration
FROM
(
SELECT Person, Person_Number, Date, MIN(Time_Stamp) AS [Start],
CASE
WHEN MAX(Time_Stamp) <> MIN(Time_Stamp)
THEN MAX(Time_Stamp)
END AS [End] /* To select End as null when there is no End for a Start */
FROM table_name
GROUP BY Person, Person_Number, Date, (Stamp_number+1)/2
) T
ORDER BY Person_Number, Date, [Start]
See a demo.

How to calculate Total working hours of employee by each session

I need to display employee login and logout hours for each session of the day and calculate Total Working hours. If the employee login and logout many times, each session and total should be displayed.
My table will be look like this
Id
EmpId
LocationId
LogDate
AccessType
OutType
1
4545_1
4545
2022-05-25 16:27:41.217
1
NULL
2
4545_1
4545
2022-05-25 17:27:26.673
2
1
4
4545_1
4545
2022-05-25 17:31:30.333
1
NULL
5
4545_1
4545
2022-05-25 19:31:38.973
2
1
6
1212_8
1212
2022-05-26 10:21:38.973
1
NULL
6
1212_8
1212
2022-05-26 12:21:38.973
2
2
Here
AccessType 1=IN 2=OUT
OutType 1=LogOut 2=Session Out
I want the output like this
EmpId
LocationId
SessionStart
SessionEnd
Hours
4545_1
4545
2022-05-25 16:27:41.217
2022-05-25 17:27:26.673
1:00
4545_1
4545
2022-05-25 17:31:30.333
2022-05-25 19:31:38.973
2:00
1212_8
1212
2022-05-26 10:21:38.973
2022-05-26 12:21:38.973
2:00
This is what I tried
select[EmpId],
[LocationId],
ShiftDate,
SessionStartTime,
SessionEndTime
, Total_Time = right(concat('0', Total_Time / 3600), 2) + ':' + right(concat('0', Total_Time % 3600 / 60), 2)
from (
select
[EmpId],[LocationId], ShiftDate = cast(min(LogDate) as date)
, SessionStartTime = min(LogDate)
, SessionEndTime = max(LogDate)
, Total_Time = sum(ss)
from (
select
*, ss = datediff(ss, LogDate, lead(LogDate) over (partition by [EmpId], grp order by LogDate))
from (
select
*, grp = sum(diff) over (partition by [EmpId] order by LogDate)
from (
select
*, diff = iif(datediff(mi, lag(LogDate) over (partition by [EmpId] order by LogDate), LogDate) > 300 and [AccessType] = 1, 1, 0)
from
[tblEmployeeAttendance] where cast(LogDate as date) >= '2022-05-25' and cast(LogDate as date) <= '2022-05-26'
) t
) t
) t
group by [EmpId],[LocationId], grp) t
I got result like this
EmpId
LocationId
SessionStart
SessionEnd
Hours
4545_1
4545
2022-05-25 16:27:41.217
2022-05-25 19:31:38.973
3:00
1212_8
1212
2022-05-26 10:21:38.973
2022-05-26 12:21:38.973
2:00
Here the problem is I get only min login and max logout for the day in one row. But I need each login and logout session of the user on the day and total for each.
I request somebody to help on this.
You can do this with an outer apply.
I don't know what the OutType is used for, what the difference is with AccessType. You did not explain that or any other logic so I just assumed it should work on AccessType.
If that is not the case you can easy adapt the logic in the subquery below.
select e.EmpId,
e.LocationID,
e.logdate as SessionStart,
d.logdate as SessionEnd,
datediff(hour, e.logdate, d.logdate) as Hours
from emp e
outer apply ( select top 1 emp.logdate
from emp
where emp.empid = e.empid
and emp.accesstype = 2
and emp.logdate > e.logdate
order by emp.logdate
) d
where e.accesstype = 1
See the DBFiddle here
Result
EmpId
LocationID
SessionStart
SessionEnd
Hours
4545_1
4545
2022-05-25 16:27:41.217
2022-05-25 17:27:26.673
1
4545_1
4545
2022-05-25 17:31:30.333
2022-05-25 19:31:38.973
2
1212_8
1212
2022-05-26 10:21:38.973
2022-05-26 12:21:38.973
2

SQL select students logins

I have a table STUDENT_LAST_LOGIN, which contains data about students last logins.
ID STUDENT_ID DATE TIME
1 A 2020-02-01 12:00 15 MIN
2 B 2020-02-02 12:00 45 MIN
3 C 2020-02-03 12:00 25 MIN
In addition there is STUDENT_LOGIN table, which contains data about students all logins.
ID STUDENT_ID DATE TIME
1 A 2020-02-01 12:00 15 MIN
4 A 2020-01-01 14:00 33 MIN
2 B 2020-02-02 12:00 45 MIN
5 B 2020-01-02 13:30 47 MIN
10 B 2020-01-03 13:30 27 MIN
6 B 2020-01-02 10:00 44 MIN
3 C 2020-02-03 12:00 25 MIN
7 C 2020-01-03 10:00 12 MIN
8 C 2020-01-03 18:00 56 MIN
9 C 2020-01-04 12:00 88 MIN
As a result I need to get something like this:
STUDENT_ID LAST_LOGIN LAST_LOGIN_ONE_MONTH_AGO TIME TIME_ONE_MONTH_AGO
A 2020-02-01 12:00 2020-01-01 14:00 15 min 33 min
B 2020-02-02 12:00 2020-01-02 13:30 15 min 47 min
C 2020-02-03 12:00 2020-01-03 18:00 25 min 56 min
Can you help me write this?
SELECT LAST_LOGIN, LAST_LOGIN_ONE_MONTH_AGO, S_L.TIME, S_L.TIME_ONE_MONTH_AGO
FROM STUDENT_LAST_LOGIN S_L_L
INNER JOIN STUDENT_LOGIN S_L on S_L_L.id = S_L.id
where S_L_L.date < DATEADD(month, -1, GETDATE())
you need to write your query something like this.
You need to use the windows function as follows:
SELECT * FROM
(SELECT SLL.STUDENT_ID,
SLL.DATE LAST_LOGIN,
SL.DATE LAST_LOGIN_ONE_MONTHE_AGO,
SLL.TIME,
SL.TIME TIME_ONE_MONTH_AGO,
ROW_NUMBER() OVER (PARTITION BY SLL.STUDENT_ID ORDER BY SL.DATE DESC NULLS LAST) AS RN
FROM STUDENT_LAST_LOGIN SLL LEFT JOIN STUDENT_LOGIN SL
ON SL.STUDENT_ID = SLL.STUDENT_ID
AND TRUNC(SL.DATE) = ADD_MONTHS(TRUNC(SLL.DATE),-1)
)
WHERE RN = 1
I can only speculate that you want the most recent login and then the most recent login from the calendar month before that. I would suggest conditional aggregation:
select sll.student_id,
max(case when month_seqnum = 1 then last_login end),
max(case when month_seqnum = 2 then last_login end),
max(case when month_seqnum = 1 then time end),
max(case when month_seqnum = 2 then time end)
from (select sll.*,
row_number() over (partition by student_id, to_char(date, 'YYYY-MM')
order by date desc
) as seqnum,
dense_rank() over (partition by student_id order by to_char(date, 'YYYY-MM')) as month_seqnum
from student_last_login sll
) sll
where month_seqnum in (1, 2) and seqnum = 1
group by student_id;
I think this returns the values that you specify.

MsSql Compare specific datetimes in sequence based on ID

I have a table where we store our data from a call and it looks like this:
CallID Arrive_Seq DateTime ActivitytypeID
1 1 2018-01-01 05:00:00 1
1 2 2018-01-01 05:00:01 2
1 3 2018-01-01 06:00:00 21
1 4 2018-01-01 06:00:01 28
1 5 2018-01-01 06:00:02 13
1 6 2018-01-01 06:00:03 22
1 7 2018-01-01 06:00:05 29
1 8 2018-01-01 06:05:00 21
1 9 2018-01-01 06:05:01 28
1 10 2018-01-01 06:05:02 13
1 11 2018-01-01 06:05:03 22
1 12 2018-01-01 06:07:45 29
Now I want to select the datediff between ActivitytypeID 21 and 29 in the arrive_sew order. In this example they occur twice (on arrive_seq 3,8 and 7,12). This order is not specific and ActivitytypeID can occur both more and less times in the sequence but they are always connected with eachother. Think of it as ActivitytypeID 21 = 'call started' AND ActivitytypeID = 29 'Call ended'.
In the example the answer whould be:
SELECT DATEDIFF (SECOND, '2018-01-01 06:00:00', '2018-01-01 06:00:05') = 5 -- Compares datetime of arrive_seq 3 and 7
AND
SELECT DATEDIFF (SECOND, '2018-01-01 06:00:05', '2018-01-01 06:07:45') = 460 -- Compares datetime of arrive_seq 21 and 29
Total duration = 465
I have tried with this code but it doesn't work all the time due to row# can change based on arrive_seq and ActivitytypeID
;WITH CallbackDuration AS (
SELECT ROW_NUMBER() OVER(ORDER BY a.time_stamp ASC) AS RowNumber, DATEDIFF(second, a.time_stamp, b.time_stamp) AS 'Duration'
FROM Table a
JOIN Table b on a.call_id = b.call_id
WHERE a.call_id = 1 AND a.activity_type = 21 AND b.activity_type = 29
GROUP BY a.time_stamp, b.time_stamp,a.call_id)
SELECT SUM(Duration) AS 'Duration' FROM CallbackDuration WHERE RowNumber in (1,5,9)
I think this is what you want:
select
call_start,
call_end,
datediff (second, call_start, call_end) as duration
from
(
select
call_timestamp as call_end,
lag(call_timestamp) over (partition by call_id order by call_timestamp) as call_start,
activity_type as call_end_activity,
lag (activity_type) over (partition by call_id order by call_timestamp) as call_start_activity
from
call_log
where
activity_type in (21, 29)
) x
where
call_start_activity = 21;
Result:
call_start call_end duration
----------------------- ----------------------- -----------
2018-01-01 06:00:00.000 2018-01-01 06:00:05.000 5
2018-01-01 06:05:00.000 2018-01-01 06:07:45.000 165
(2 rows affected)
Note that the time of the second call is based on your sample data with start time 2018-01-01 06:05:00
This query seems to return your expected result
declare #x int = 21
declare #y int = 29
;with cte(CallID, Arrive_Seq, DateTime, ActivitytypeID) as (
select
a, b, cast(c as datetime), d
from (values
(1,1,'2018-01-01 05:00:00',1)
,(1,2,'2018-01-01 05:00:01',2)
,(1,3,'2018-01-01 06:00:00',21)
,(1,4,'2018-01-01 06:00:01',28)
,(1,5,'2018-01-01 06:00:02',13)
,(1,6,'2018-01-01 06:00:03',22)
,(1,7,'2018-01-01 06:00:05',29)
,(1,8,'2018-01-01 06:05:00',21)
,(1,9,'2018-01-01 06:05:01',28)
,(1,10,'2018-01-01 06:05:02',13)
,(1,11,'2018-01-01 06:05:03',22)
,(1,12,'2018-01-01 06:07:45',29)
) t(a,b,c,d)
)
select
sum(ss)
from (
select
*, ss = datediff(ss, DateTime, lead(datetime) over (order by Arrive_Seq))
, rn = row_number() over (order by Arrive_Seq)
from
cte
where
ActivitytypeID in (#x, #y)
) t
where
rn % 2 = 1

Conditional time to status calculation

I am trying to calculate how long it takes a rep to have x amount of clients apply for service: meaning I need the time between date_created - ie. date the rep was onboarded, and when rep reaches a certain "status". Status is reached when x of the rep's clients (= users) have a non-null date_applied- ie. date user signed up.
x is minimum criteria to reach each "status", and ties back to a previous question: Aggregate case when inside non aggregate query where I am currently calculating "status" like so:
case when count(date_applied) over (partition by rep_id) >=10 then 'status1'
when count(date_applied) over (partition by rep_id) >=5 then 'status2'
when count(date_applied) over (partition by rep_id) >=1 then 'status3'
else 'no_status' end status
So it takes 10 clients to reach status1, 5 to reach status2 and 1 to reach status3. These are the criteria for each "status", so if you have 7 users for example, you still calculate status2 based on the date the 5th user applied.
I think calculating time_to_status1/2/3 (what i am trying to get at) should look something like this:
case when count(date_applied) over (partition by rep_id) >=10 then
datediff(day, date_created, date_applied for the 10th user that applied with that rep) end as time_to_status1,
case when count(date_applied) over (partition by rep_id) >=5 then
datediff(day, date_created, date_applied for the 5th user that applied with that rep) end as time_to_status2,
case when count(date_applied) over (partition by rep_id) >=1 then
datediff(day, date_created, date_applied for the 1st user that applied with that rep) end as time_to_status3
Any help is greatly appreciated!
--Edit--
Sample current data:
rep_id user_id date_created date_applied status
1 1 1/1/2018 6:43:22 AM 1/5/2018 2:45:15 PM status2
1 2 1/1/2018 6:43:22 AM 1/5/2018 3:35:15 PM status2
1 3 1/1/2018 6:43:22 AM 1/6/2018 4:25:15 PM status2
1 4 1/1/2018 6:43:22 AM 1/7/2018 5:05:15 PM status2
1 5 1/1/2018 6:43:22 AM 1/10/2018 3:35:15 PM status2
1 6 1/1/2018 6:43:22 AM 1/15/2018 12:55:23 PM status2
2 7 1/12/2018 1:13:42 PM 1/15/2018 4:25:15 PM status3
2 8 1/12/2018 1:13:42 PM 1/16/2018 1:05:15 PM status3
2 9 1/12/2018 1:13:42 PM 1/16/2018 3:35:15 PM status3
3 10 1/20/2018 10:13:15 AM 1/26/2018 7:25:15 PM status3
4 11 1/21/2018 3:33:23 PM (null) no_status
Desired output:
rep_id user_id date_created date_applied status time_to_status1 time_to_status2 time_to_status3
1 1 1/1/2018 6:43:22 AM 1/5/2018 2:45:15 PM status2 (null) 9 (null)
1 2 1/1/2018 6:43:22 AM 1/5/2018 3:35:15 PM status2 (null) 9 (null)
1 3 1/1/2018 6:43:22 AM 1/6/2018 4:25:15 PM status2 (null) 9 (null)
1 4 1/1/2018 6:43:22 AM 1/7/2018 5:05:15 PM status2 (null) 9 (null)
1 5 1/1/2018 6:43:22 AM 1/10/2018 3:35:15 PM status2 (null) 9 (null)
1 6 1/1/2018 6:43:22 AM 1/15/2018 12:55:23 PM status2 (null) 9 (null)
2 7 1/12/2018 1:13:42 PM 1/15/2018 4:25:15 PM status3 (null) (null) 3
2 8 1/12/2018 1:13:42 PM 1/16/2018 1:05:15 PM status3 (null) (null) 3
2 9 1/12/2018 1:13:42 PM 1/16/2018 3:35:15 PM status3 (null) (null) 3
3 10 1/20/2018 10:13:15 AM 1/26/2018 7:25:15 PM status3 (null) (null) 6
4 11 1/21/2018 3:33:23 PM (null) no_status (null) (null) (null)
rep_id=1 has status2 because he has 6 users with with a non null date_applied, so time_to_status2 in his case is based on date_applied of 5th client rep signed up: datediff(day, '1/1/2018 6:43:22 AM', '1/10/2018 3:35:15 PM') = 9 days
rep_id=2 has status3 because he has 3 users with a non null date_applied, so time_to_status3 in his case is based on date_applied of 1st client rep signed up: datediff(day, '1/12/2018 1:13:42 PM', '1/15/2018 4:25:15 PM') = 3 days
rep_id=3 has status3 because he has 1 (>=1) user with a non null date_applied, so time_to_status3 in his case is datediff(day, '1/20/2018 10:13:15 AM', '1/26/2018 7:25:15 PM') = 6 days
Based on #Parfait's deleted hint, and #Gordon's answer on a different question, I was able to come up with an answer:
with cte as
(
initial query with:
case when count(client_signup_date) over (partition by rep_id) >=10 then 'status1'
when count(client_signup_date) over (partition by rep_id) >=5 then 'status2'
when count(client_signup_date) over (partition by rep_id) >=1 then 'status3'
else 'none' end status,
row_number() over(partition by rep_id order by client_signup_date) as rank
)
select *,
max(case when status = 'status1' and rank = 10
then datediff(day, advisor_onboard_date, client_signup_date)
end) over (partition by rep_id) as time_to_status1,
max(case when status = 'status2' and rank = 5
then datediff(day, advisor_onboard_date, client_signup_date)
end) over (partition by rep_id) as time_to_status2,
max(case when status = 'status3' and rank = 1
then datediff(day, advisor_onboard_date, client_signup_date)
end) over (partition by rep_id) as time_to_status3
into #t
from cte