Determine overlapping times between record sets - sql

I have 2 sets of data I need to compare via time stamps. I need to determine how long a staff member is with a patient in a specific room. Initially, we were told the patient arrives to a room first and is the last to leave. This is not the case looking at our data now. This is the query we were using:
Select...
Inner Join Staff on Staff.LocationId = Patient.LocationId
Where Staff.StartTime >= Patient.StartTime and Staff.EndTime <= Patient.EndTime
While this query works for those scenarios, there are times where the staff is already in the room and may leave later or earlier. What we want to gather is the time at which both people are in the room to the time they are no longer together.
Given the example below, the staff member is already in the room before the patient. The staff member leaves before the Patient has left.
PatientId LocationId LocationName StartTimeInRoom EndTimeInRoom
========= ========== ============ =============== =============
7109 19 Testing 2015-05-19 10:02:11.483 2015-05-19 10:36:24.617
UserId LocationId LocationName StartTimeInRoom EndTimeInRoom
====== ========== ============ =============== =============
27 19 Testing 2015-05-19 10:00:11.900 2015-05-19 10:03:41.547
The desired outcome would look like this:
PatientId UserId LocationId LocationName TimeWithPatient StartTimeInRoom EndTimeInRoom
========= ====== ========== ============ =============== =============== =============
7109 27 19 Testing 90 2015-05-19 10:02:11.483 2015-05-19 10:03:41.547
So the above scenario is when the Staff member is already in the room. So, we can use the time at which the patient enters the room until the staff member leaves.
The problem I am having is figuring out if there is an overlap and if one exists between a patient and a staff member then determine which dates to use for their visit.
I have tried creating separate queries for each scenario, but it did not help me to determine the overlaps.
Update:
I wonder if this would work:
Select...
Inner Join Staff on Staff.LocationId = Patient.LocationId
where (Staff.StartTime BETWEEN Patient.StartTime and Patient.EndTime)
or (Patient.StartTime between Staff.StartTime and Staff.EndTime)

You can use inequalities in the join condition to get overlaps. Then use case to get the earliest and latest time:
Select (case when p.StartTime > s.StartTime then p.StartTime else s.StartTime
end) as StartTime,
(case when p.EndTime < s.EndTime then p.EndTime else s.EndTime
end) as EndTime
from Patient p Inner Join
Staff s
on s.LocationId = p.LocationId qne
s.StartTime <= p.EndTime and s.EndTime >= p.StartTime;
Note: If a patient and/or staff enters a room multiple times without the other leaving, then this will produce the longest time from the first time both were together to the last time. If this is an issue, then ask another question with sample data and desired results.

with cte (startime,endtime)
as
(
select
(select top 1 startime from staff where staffid = 27 and locationid = 19
and startime > '2015-05-18' order by startime),
(select top 1 endtime from staff where staffid = 27 and locationid = 19 and
startime > '2015-05-18' order by endtime desc)
)
select * from cte
Let me know if I am heading in the right direction, what this code does is shows the first time the staff entered the room on the day (let's say we are querying the database on 19/05/2015) and the last time they left...using the inner join it could be modified to show the first and last time the staff entered the room whilst the patient was in there. I realise it might have to work the other way round (ie staff entered first) and the various combinations, but these could all be incorporated into modified versions of this. Of course we are dealing only with the time from when the second individual to enter the room, entered the room, to the time when the first individual to leave the room, left the room, there would be no difficulty with adding up only the times they were in the room together if you wish...

Related

How to add custom YoY field to output?

I'm attempting to determine the YoY growth by month, 2017 to 2018, for number of Company bookings per property.
I've tried casting and windowed functions but am not obtaining the correct result.
Example Table 1: Bookings
BookID Amnt BookType InDate OutDate PropertyID Name Status
-----------------------------------------------------------------
789555 $1000 Company 1/1/2018 3/1/2018 22111 Wendy Active
478141 $1250 Owner 1/1/2017 2/1/2017 35825 John Cancelled
There are only two book types (e.g., Company, Owner) and two Book Status (e.g., Active and Cancelled).
Example Table 2: Properties
Property ID State Property Start Date Property End Date
---------------------------------------------------------------------
33111 New York 2/3/2017
35825 Michigan 7/21/2016
The Property End Date is blank when the company still owns it.
Example Table 3: Months
Start of Month End of Month
-------------------------------------------
1/1/2018 1/31/2018
The previous developer created this table which includes a row for each month from 2015-2020.
I've tried many various iterations of my current code and can't even come close.
Desired Outcome
I need to find the YoY growth by month, 2017 to 2018, for number of Company bookings per property. The stakeholder has requested the output to have the below columns:
Month Name Bookings_Per_Property_2017 Bookings_Per_Property_2018 YoY
-----------------------------------------------------------------------
The number of Company bookings per property in a month should be calculated by counting the total number of active Company bookings made in a month divided by the total number of properties active in the month.
Here is a solution that should be close to what you need. It works by:
LEFT JOINing the three tables; the important part is to properly check the overlaps in date ranges between months(StartOfMonth, EndOfMonth), bookings(InDate, OutDate) and properties(PropertyStartDate, PropertyEndDate): you can have a look at this reference post for general discussion on how to proceed efficiently
aggregating by month, and using conditional COUNT(DISTINCT ...) to count the number of properties and bookings in each month and year. The logic implicitly relies on the fact that this aggregate function ignores NULL values. Since we are using LEFT JOINs, we also need to handle the possibility that a denominator could have a 0 value.
Notes:
you did not provide expected results so this cannot be tested
also, you did not explain how to compute the YoY column, so I left it alone; I assume that you can easily compute it from the other columns
Query:
SELECT
MONTH(m.StartOfMonth) AS [Month],
COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2017 THEN b.BookID END)
/ NULLIF(COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2017 THEN p.PropertyID END), 0)
AS Bookings_Per_Property_2017,
COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2018 THEN b.BookID END)
/ NULLIF(COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2018 THEN p.PropertyID END), 0)
AS Bookings_Per_Property_2018
FROM months m
LEFT JOIN bookings b
ON m.StartOfMonth <= b.OutDate
AND m.EndOfMonth >= b.InDate
AND b.status = 'Active'
AND b.BookType = 'Company'
LEFT JOIN properties p
ON m.StartOfMonth <= COLAESCE(p.PropertyEndDate, m.StartOfMonth)
AND m.EndOfMonth >= p.PropertyStartDate
GROUP BY MONTH(m.StartOfMonth)

count occurrences for each week using db2

I am looking for some general advice rather than a solution. My problem is that I have a list of dates per person where due to administrative procedures, a person may have multiple records stored for this one instance, yet the date recorded is when the data was entered in as this person is passed through the paper trail. I understand this is quite difficult to explain so I'll give an example:
Person Date Audit
------ ---- -----
1 2000-01-01 A
1 2000-01-01 B
1 2000-01-02 C
1 2003-04-01 A
1 2003-04-03 A
where I want to know how many valid records a person has by removing annoying audits that have recorded the date as the day the data was entered, rather than the date the person first arrives in the dataset. So for the above person I am only interested in:
Person Date Audit
------ ---- -----
1 2000-01-01 A
1 2003-04-01 A
what makes this problem difficult is that I do not have the luxury of an audit column (the audit column here is just to present how to data is collected). I merely have dates. So one way where I could crudely count real events (and remove repeat audit data) is to look at individual weeks within a persons' history and if a record(s) exists for a given week, add 1 to my counter. This way even though there are multiple records split over a few days, I am only counting the succession of dates as one record (which after all I am counting by date).
So does anyone know of any db2 functions that could help me solve this problem?
If you can live with standard weeks it's pretty simple:
select
person, year(dt), week(dt), min(dt), min(audit)
from
blah
group by
person, year(dt), week(dt)
If you need seven-day ranges starting with the first date you'd need to generate your own week numbers, a calendar of sorts, e.g. like so:
with minmax(mindt, maxdt) as ( -- date range of the "calendar"
select min(dt), max(dt)
from blah
),
cal(dt,i) as ( -- fill the range with every date, count days
select mindt, 0
from minmax
union all
select dt+1 day , i+1
from cal
where dt < (select maxdt from minmax) and i < 100000
)
select
person, year(blah.dt), wk, min(blah.dt), min(audit)
from
(select dt, int(i/7)+1 as wk from cal) t -- generate week numbers
inner join
blah
on t.dt = blah.dt
group by person, year(blah.dt), wk

SQL Server - Need to obtain duplicate records based on mutiple criteria of the same column

I work with a huge dataset of hospital activity records. Each record represents something done on behalf of a patient. My focus is on patients that have experienced 'outpatient' activity, such as attended an appointment or clinic.
In the data, we get records that are duplicates in that; a patient is shown to have attended their first out patient appointment more than once in a six month period. This is an error on the part of the hospital who send their data. We have to identify these records to send back as challenges.
I have the following SQL statement which is finding records where the 'Patient Code' appears more than once.
SELECT * FROM dbo.Z_ForQueries a
JOIN (SELECT PatientCode
FROM dbo.Z_ForQueries
GROUP BY PatientCode
HAVING COUNT (*) > 1 ) b
ON a.PatientCode = b.PatientCode
WHERE [Multiple OPFA in month] = 'y'
I cannot for the life of me figure out how to syntax the next bit; For each set of duplicated patient codes, I only want to see the records where one of the records has a 'Month' of 7 (that's the just the current month I'm working on). If non of the groups of duplicated records have '7' in the month, then I don't need to see them.
For example, patient code L000066715 has 4 records, I can see that each record represents the same initial outpatient appointment in the same hospital speciality. Obviously you can only 'first attend' once. Each record has a month number; 3,4,6 & 7. Because this patient code has one of their duplicate records in month 7, I need it to be returned in the results along with the other 3 records.
Other patient codes exist in duplicate but none of their records are from month 7, so they don't need to be returned.
I hope I've set the scene properly for some help! Thanks.
Something like this should work:
SELECT *
FROM dbo.Z_ForQueries a
JOIN (
SELECT PatientCode,
MAX(CASE WHEN MONTH(dateColumn) = 7 THEN 1 ELSE 0 END) As InMonth
FROM dbo.Z_ForQueries
GROUP BY PatientCode
HAVING COUNT (*) > 1
) b ON a.PatientCode = b.PatientCode
And InMonth = 1
WHERE [Multiple OPFA in month] = 'y'
Explanation:
The CASE expression returns 1 for rows where Month=7, and 0 in all other cases. The MAX(..) around this CASE expressions thus returns 1 if any rows in the GROUP had a Month=7 and a 0 only if none of them did.

Getting repeated rows for where with or condition

I am trying find employees that worked during a specific time period and the hours they worked during that time period. My query has to join the employee table that has employee id as pk and uses effective_date and expiration_date as time measures for the employee's position to the timekeeping table that has a pay period id number as pk and also uses effective and expiration dates.
The problem with the expiration date in the employee table is that if the employee is currently employed then the date is '12/31/9999'. I am looking for employees that worked in a certain year and current employees as well as the hours they worked separated by pay periods.
When I take this condition in account in the where with an OR statement, I get duplicates that is employees that have worked the time period I am looking for and beyond as well as duplicate records for the '12/31/9999' and the valid employee in that time period.
This is the query I am using:
SELECT
J.EMPL_ID
,J.DEPT
,J.UNIT
,J.LAST_NM
,J.FIRST_NM
,J.TITLE
,J.EFF_DT
,J.EXP_DT
,TM1.PPRD_ID
,TM1.EMPL_ID
,TM1.EXP_DT
,TM1.EFF_DT
--PULLING IN THE DAILY HRS WORKED
,(SELECT NVL(SUM(((to_number(SUBSTR(TI.DAY_1, 1
,INSTR(TI.DAY_1, ':', 1, 1)-1),99))*60)+
(TO_NUMBER(SUBSTR(TI.DAY_1
,INSTR(TI.DAY_1,':', -1, 1)+1),99))),0)
FROM PPRD_LINE TI
WHERE
TI.PPRD_ID=TM1.PPRD_ID
) "DAY1"
---AND THE REST OF THE DAYS FOR THE WORK PERIOD
FROM PPRD_LINE TM1
JOIN EMPL J ON TM1.EMPL_ID=J.EMPL_ID
WHERE
J.EMPL_ID='some id number' --for test purposes, will need to break down to depts-
AND
J.EFF_DT >=TO_DATE('1/1/2012','MM/DD/YYYY')
AND
(
J.EXP_DT<=TO_DATE('12/31/2012','MM/DD/YYYY')
OR
J.EXP_DT=TO_DATE('12/31/9999','MM/DD/YYYY') --I think the problem might be here???
)
GROUP BY
J.EMPL_ID
,J.DEPT
,J.UNIT
,J.LAST_NM
,J.FIRST_NM
,J.TITLE
,J.EFF_DT
,J.EXP_DT
,TM1.PPRD_ID
,TM1.EMPL_ID
,TM1.DOC_ID
,TM1.EXP_DT
,TM1.EFF_DT
ORDER BY
J.EFF_DT
,TM1.EFF_DT
,TM1.EXP_DT
I'm pretty sure I'm missing something simple but at this point I can't see the forest for the trees. Can anyone out there point me in the right direction?
an example of the duplicate records:
for employee 1 for the year of 2012:
Empl_ID Dept Unit Last First Title Eff Date Exp Date PPRD ID Empl_ID
00001 04 012 Babbage Charles Somejob 4/1/2012 10/15/2012 0407123 00001
Exp Date_1 Eff Date_1
4/15/2012 4/1/2012
this record repeats 3 times and goes past the pay periods in 2012 to the current pay period in 2013
the subquery I use to convert time to be able to add hrs and mins together to compare down the line.
I'm going to take a wild guess and see if this is what you want, remember I could not test so there may be typos.
If this is and especially if it is not, you should read in the FAQ about how to ask good questions. If this is what you were trying to understand your question should have been answered within about 10 mins. Because it was not clear what you were asking no one could answer your question.
You should include inputs and outputs and EXPECTED output in your question. The data you gave was not the output of the select statement (it did not have the DAY1 column).
SELECT
J.EMPL_ID
,J.DEPT
,J.UNIT
,J.LAST_NM
,J.FIRST_NM
,J.TITLE
,J.EFF_DT
,J.EXP_DT
,TM1.PPRD_ID
,TM1.EMPL_ID
-- ,TM1.EXP_DT Can't have these if you are summing accross multiple records.
-- ,TM1.EFF_DT
--PULLING IN THE DAILY HRS WORKED
,NVL(SUM(((to_number(SUBSTR(TM1.DAY_1, 1,INSTR(TM1.DAY_1, ':', 1, 1)-1),99))*60)+
(TO_NUMBER(SUBSTR(TM1.DAY_1,INSTR(TM1.DAY_1,':', -1, 1)+1),99))),0)
"DAY1"
---AND THE REST OF THE DAYS FOR THE WORK PERIOD
FROM PPRD_LINE TM1
JOIN EMPL J ON TM1.EMPL_ID=J.EMPL_ID
WHERE
J.EMPL_ID='some id number' --for test purposes, will need to break down to depts-
AND J.EFF_DT >=TO_DATE('1/1/2012','MM/DD/YYYY')
AND(J.EXP_DT<=TO_DATE('12/31/2012','MM/DD/YYYY') OR J.EXP_DT=TO_DATE('12/31/9999','MM/DD/YYYY'))
GROUP BY
J.EMPL_ID
,J.DEPT
,J.UNIT
,J.LAST_NM
,J.FIRST_NM
,J.TITLE
,TM1.PPRD_ID
,TM1.EMPL_ID
,TM1.DOC_ID
ORDER BY
MIN(J.EFF_DT)
,MAX(TM1.EFF_DT)
,MAX(TM1.EXP_DT)

Query to calculate time between multiple trips

We have a table that stores the number of trip and type of trip made for a given day by a driver as follows
Date Delivery Pick-up
==== ======== =======
01/01/2013 5 0
We also have an attendance table that stores the driver attendance as follows.
AttDate InTime OutTIme THours
======= ====== ======= ======
01/01/2013 10:00 13:00 3
How do I calculate the average time between the 5 trip using the employee Thours using MSSQL 2008/2012?. This is for performance monitoring purpose.
I'm assuming that you have some sort of driver_id, which you'll have to add to the join, or, as #Randy points out, you won't be able to determine which rows belong to which drivers.
Here's the general form of the query:
SELECT Trip.business_day,
DATEDIFF(minute, arrivedAt, leftBy) / CASE WHEN deliveries = 0
THEN 1
ELSE deliveries END
as average_deliveries_in_minutes
FROM Trip
JOIN Driver_Attendence
ON Driver_Attendence.business_day = Trip.business_day
(working SQL Fiddle example)
You didn't actually list what you wanted to do deliveries = 0 is true - the CASE is there so you don't get 'divide-by-zero' errors. Excluding drivers without any deliveries would allow you to remove the case, and just reference the column.