Reputation: 103
Can you please help me with the following.
I am trying to calculate how may shifts a patient was in the hospital. The shifts timings start from 7:00 AM
to 6:59 PM
and 7:00 PM
to 6.59 AM
.
If a patient was admitted to a location after the start of the shift, we ignore that shift in the total calculation.
Here is the sample data and how the end result should look like :
DECLARE @T AS TABLE
(
ID INT,
LOCATION VARCHAR(10),
Date_entered DATETIME,
date_left datetime
);
DECLARE @endresult AS TABLE
(
ID INT,
LOCATION VARCHAR(10),
Date_entered DATETIME,
date_left datetime,
Total_shifts int
)
insert into @T VALUES
(1,'AB','01/01/2019 07:10','01/01/2019 20:30'),
(2,'CD','01/01/2019 20:30','01/04/2019 02:30'),
(3,'EF','01/04/2019 02:30','01/07/2019 19:30'),
(4,'GH','01/07/2019 19:30','01/08/2019 13:30')
insert into @endresult VALUES
(1,'AB','01/01/2019 07:10','01/01/2019 20:30',1),
(2,'CD','01/01/2019 20:30','01/04/2019 02:30',4),
(3,'EF','01/04/2019 02:30','01/07/2019 19:30',8),
(4,'GH','01/07/2019 19:30','01/08/2019 13:30',1)
SELECT * FROM @t
select * from @endresult
I tried using a recursive CTE, but the query is taking too much time to complete. Any simple way to calculate the timings?
Upvotes: 0
Views: 73
Reputation: 222582
Here is a query that returns the correct results for your sample data :
select
t.*,
DATEDIFF(DAY, date_entered, date_left) * 2
- CASE WHEN DATEPART(HOUR, date_entered) < 7 THEN 0 WHEN DATEPART(HOUR, date_entered) < 19 THEN 1 ELSE 2 END
+ CASE WHEN DATEPART(HOUR, date_left) < 7 THEN 0 WHEN DATEPART(HOUR, date_left) < 19 THEN 1 ELSE 2 END
AS Total_shifts
from @t t;
The logic is to first count how many days occured between entrance and exit, then multiply it by two to get a raw number of shifts ; then adjust the raw count by checking the times when the patient entered and exited.
This demo on DB Fiddle with your sample data returns :
ID | LOCATION | Date_entered | date_left | Total_shifts -: | :------- | :------------------ | :------------------ | -----------: 1 | AB | 01/01/2019 07:10:00 | 01/01/2019 20:30:00 | 1 2 | CD | 01/01/2019 20:30:00 | 04/01/2019 02:30:00 | 4 3 | EF | 04/01/2019 02:30:00 | 07/01/2019 19:30:00 | 8 4 | GH | 07/01/2019 19:30:00 | 08/01/2019 13:30:00 | 1
Upvotes: 2
Reputation: 147206
Here's a query that will give you the results you want. It uses 3 CTEs, the first simply selects the minimum Date_entered
and maximum date_left
values, the second aligns the minimum Date_entered
value to a shift start time (7AM or 7PM), and the third recursive CTE generates a list of all the shift start times between the minimum Date_entered
and maximum date_left
values. Finally we JOIN that CTE to the admissions table and count the number of shift start times between Date_entered
and date_left
:
WITH cte AS
(SELECT MIN(Date_entered) AS min_date, MAX(date_left) AS max_date
FROM T),
cte1 AS
(SELECT CASE WHEN DATEPART(hour, min_date) < 7 THEN DATEADD(hour, 19, DATEADD(day, -1, CONVERT(DATE, min_date)))
ELSE DATEADD(hour, 7, CONVERT(DATETIME, CONVERT(DATE, min_date)))
END AS min_shift
FROM cte),
shifts AS
(SELECT min_shift AS shift_start
FROM cte1
UNION ALL
SELECT DATEADD(hour, 12, shift_start)
FROM shifts
WHERE shift_start < (SELECT max_date FROM cte))
SELECT T.ID, T.LOCATION, T.Date_Entered, T.date_left, COUNT(s.shift_start) AS Total_shifts
FROM T
JOIN shifts s ON s.shift_start BETWEEN T.Date_Entered AND T.date_left
GROUP BY T.ID, T.LOCATION, T.Date_Entered, T.date_left
Output:
ID LOCATION Date_Entered date_left Total_shifts
1 AB 01/01/2019 07:10:00 01/01/2019 20:30:00 1
2 CD 01/01/2019 20:30:00 04/01/2019 06:59:00 4
3 EF 04/01/2019 07:00:00 07/01/2019 19:30:00 8
4 GH 07/01/2019 19:30:00 08/01/2019 13:30:00 1
Upvotes: 1