Exclude overlapped time period - sql

I wish to write a query for below problem.
The problem is, I want to eliminate all overlapping periods, so that I get the total amount of time which is not taken in any other row.
Example:
NAME
Start Date Time
End Date time
Load shed
21-03-2020 12:30
21-03-2020 13:30
Shutdown
21-03-2020 13:00
21-03-2020 14:00
breakdown
21-03-2020 13:10
21-03-2020 14:10
Load shed
24-03-2020 12:30
24-03-2020 13:30
Shutdown
24-03-2020 11:00
24-03-2020 19:00
breakdown
24-03-2020 13:10
24-03-2020 14:10
Now what we have to do is:
Return time period between start date time and end date time but exclude overlapped time.
Expected result will be:
NAME
Start Date Time
End Date time
Time_interval
Load shed
21-03-2020 12:30
21-03-2020 13:30
01:00
Shutdown
21-03-2020 13:30
21-03-2020 14:00
00:30
breakdown
21-03-2020 14:00
21-03-2020 14:10
00:10
Shutdown
24-03-2020 11:00
24-03-2020 19:00
08:00
Now we can see in result,
First row: As it is because it has the lowest start date time in all
overlapped rows.
Second row: 30 minutes already used in first row so
we exclude 30 minutes here and write left time interval.
Third row:
we exclude till time 14:00 because its already used in row 2 so now time
interval has 10 minutes only.
Fourth row: We exclude all rows from
given table because they all overlapped and they are within start date
time 24-3-2020 11:00 and 24:03:2020 19:00 .
Hope you understand the problem.
Thanks in advance.

You can calculate the previous enddt before each row. Then, if that is larger than the start date, use that for the row. And, if the duration of the row is negative, then filter out the row.
The code looks like:
select name, imputed_startdt, enddt, prev_enddt,
convert(time, dateadd(minute, datediff(minute, imputed_startdt, enddt), 0)) as duration
from (select t.*, max(enddt) over (order by startdt rows between unbounded preceding and 1 preceding) as prev_enddt
from t
) t cross apply
(values (case when prev_enddt > startdt then prev_enddt else startdt end)
) v(imputed_startdt)
where prev_enddt < enddt or prev_enddt is null;
Here is a db<>fiddle.

Related

How To Identify Continuous Spans Of Time And Compute Summary Statistics In Time-Series Data?

I have time-series data that I have wrangled into a #temp table in the format below:
Start
Finish
Value
2022-05-12 11:30
2022-05-12 11:45
28.264
2022-05-12 11:45
2022-05-12 12:00
28.262
2022-05-12 12:00
2022-05-12 12:15
28.242
2022-06-09 11:30
2022-06-09 11:45
27.862
2022-06-09 12:00
2022-06-09 12:15
13.727
2022-06-09 12:15
2022-06-09 12:30
13.717
2022-06-09 12:30
2022-06-09 12:45
13.724
2022-06-09 12:45
2022-06-09 13:00
9.1226
I want to identify spans of time in this data where the Start time of the next row overlaps with the Finish time of the current row and find the average of the values in the time span and maximum value observed in that time span. It should look like this:
Start
Finish
AvgVal
MaxVal
2022-05-12 11:30
2022-05-12 12:15
28.256
28.264
2022-06-09 11:30
2022-06-09 11:45
27.862
27.862
2022-06-09 12:00
2022-06-09 13:00
12.573
13.727
I was thinking of PARTITIONing over the table but I am not sure how to condition the partitioning on having the Start of the next row equal to the Finish of the current row. This is procedurally easy to write, but I have to do this in a stored procedure so I would like to do it entirely descriptively.
Perhaps I could first add ROW_NUMBER() OVER (ORDER BY Start ASC) as Row_Num to my initial table, then:
SELECT Start, Finish, AVERAGE(Value) AS AvgVal, MAX(Value) AS MaxVal
FROM #temp
PARTITION BY
... something where the row number is used to index into the next row to compare the next row's Start time with the current row's Finish time?
Any help is appreciated.
First you need to identify the continuous group of time series. You can use LAG() and compare previous Finish with current row Start. Perform a cumulative sum() and you gets the grouping required. After that it is just a normal GROUP BY query to get the AVG() and MAX()
select [Start] = min([Start]),
[Finish] = max([Finish]),
[AvgVal] = avg([Value]),
[MaxVal] = max([Value])
from
(
select *,
[grp] = sum(g) over (order by [Start])
from
(
select *,
[g] = case when [Start] = lag([Finish]) over (order by [Start])
then 0
else 1
end
from #temp
) t
) t
group by [grp]
order by [Start]

Generate rows with time intervals between 2 dates in Oracle

I have table in which Sunday to Saturdy "Doctor Start" and "End Time" is given.
I want to create time slots of 15 minutes.
On the basis of that, the patient clicks on calendar datetime interval which shows slots that have already been booked.
The following example shows how to split time into slices of 15 minutes. It uses hierarchical query. A little bit of explanation:
line 2: trunc function, applied to a date value, returns "beginning" of that day (at midnight). Adding 15 / (24*60) adds 15 minutes (as there are 24 hours in a day and 60 minutes in an hour). Multiplying 15 by level works as a "loop", i.e. adds 15-by-15-by-15 ... minutes to previous value.
line 4: similar to line 2, but it makes sure that a day (24 hours * 60 minutes) is divided to 15-minutes parts
line 6: start time is trivial
line 7: end time just adds 15 minutes to start_time
line 9: return only time between 10 and 16 hours (you don't have patients at 02:15 AM, right?)
SQL> with fifteen as
2 (select trunc(sysdate) + (level * 15)/(24*60) c_time
3 from dual
4 connect by level <= (24*60) / 15
5 )
6 select to_char(c_time, 'hh24:mi') start_time,
7 to_char(c_time + 15 / (24 * 60), 'hh24:mi') end_time
8 from fifteen
9 where extract(hour from cast (c_time as timestamp)) between 10 and 15;
START_TIME END_TIME
---------- ----------
10:00 10:15
10:15 10:30
10:30 10:45
10:45 11:00
11:00 11:15
11:15 11:30
11:30 11:45
11:45 12:00
12:00 12:15
12:15 12:30
12:30 12:45
12:45 13:00
13:00 13:15
13:15 13:30
13:30 13:45
13:45 14:00
14:00 14:15
14:15 14:30
14:30 14:45
14:45 15:00
15:00 15:15
15:15 15:30
15:30 15:45
15:45 16:00
24 rows selected.
SQL>

Split a time duration into time segments

I have an incident start time and end time e.g Sr=tart time of 15/01/2018 11:30 and end time of 16/01/2018 02:40 in an excel table.
How can I split this time range into different time segments. The segments are:-
06:00 - 11:59, 12:00 - 14:59, 15:00 - 17:59, 18:00 - 22:59, 23:00 - 05:59
For 06:00 - 11:59 I would expect 0.50 as this is 30 mins.
For 12:00 - 14:59 I would expect 3.0 for 3 hours.
For 15:00 - 17:59 I would expect 3.0 for 3 hours again.
For 18:00 - 22:59 this should be 5hrs
and for 23:00 - 02:40 should be 3.67 hrs.
What formula would I need to achieve this?
For the example you give with the layout as shown below this formula should work:
=MIN($D3-F$1,MIN(F$2-F$1,IF($C3<F$2,F$2-$C3+SUM($E3:E3),)))
The formula in C3 is:
=24*(MOD(A3,1))
and in D3:
=24*(MOD(B3,1)+INT(B3>INT(A3)))

sql missing date query

Having a table with these records:
01-JAN-15 10:00
01-JAN-15 11:00
01-JAN-15 13:00
01-JAN-15 14:00
01-JAN-15 15:00
01-JAN-15 18:00
01-JAN-15 19:00
It's 1h resolution, so in my example 12:00, 16:00 and, 17:00 are missing.
I would like to create an SQL query that returns something like this (missing hour start, and duration):
01-JAN-15 12:00, 01:00
01-JAN)15 16:00, 02:00
Any suggestion?
Assuming your values are datetime values, then you can do something like this:
select t.*,
datediff(hour, datetimecol, next_datetimecol)
from (select t.*,
lead(datetimecol) over (order by datetimecol) as next_datetime
from t
) t
where next_datetimecol is not null and
dateadd(hour, 1, datetimecol) < next_datetimecol;
Depending on your data types and the precision, this might not work exactly. But the idea is basically the same . . . use lead() to get the next value and do some comparisons.

Finding in between which two dates in a column the target date is

Got a table that I am trying to clean up and can't figure out how to find a record where one date falls between two dates in the actual columns
TargetDateTime Location TransferDateTime
01/01/2014 1:00 PM Room 1 01/01/2014 10:00 AM
01/01/2014 1:00 PM Room 2 01/01/2014 12:30 PM
01/01/2014 1:00 PM Room 3 01/01/2014 01:30 PM
01/01/2014 1:00 PM Room 4 01/01/2014 03:00 PM
TransferDateTime marks the time when a person was moved to the room
TargetDateTime marks some event that a person did.
In this example, TargetDateTime is 1:00 PM; therefore the event took place in Room 2 because 1:00 PM falls between 12:30 PM and 1:30 PM.
What would be the best way in SQL to select only that row and ignore the rest?
Thanks a bunch for any suggestions!
Based on your sample data and guessing that you have groups of the same TargetDateTime, the following should do it.
;WITH MyCTE AS
(
SELECT TargetDateTime,
Location,
TransferDateTime
ROW_NUMBER() OVER (PARTITION BY TargetDateTime ORDER BY TransferDateTime) AS rn
FROM TableName
WHERE TransferDateTime >= TargetDateTime
)
SELECT *
FROM MyCTE
WHERE rn = 1