find overlap between two sets of date ranges - sql

I would like to find the overlap between two sets of date ranges for every order number using Oracle SQL or PL/SQL.
The inputs are "result set one" and "result set two". The output should be "overlap".
result set one
WITH T_RESULT_SET_ONE as(
select 21365 order_number,to_date('01/01/2021 09:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 10:30:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21365 order_number,to_date('02/01/2021 14:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('02/01/2021 18:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 08:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:43:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 16:34:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 18:15:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21367 order_number,to_date('04/01/2021 15:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('04/01/2021 16:15:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
)
or
result set two
T_RESULT_SET_TWO as(
select 21365 order_number,to_date('01/01/2021 09:30:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:45:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21365 order_number,to_date('02/01/2021 13:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('02/01/2021 17:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21367 order_number,to_date('01/01/2021 09:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 10:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 16:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 19:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('05/01/2021 19:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('04/01/2021 19:46:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
)
or
overlap
T_OVERLAP as
(
select 21365 order_number,to_date('01/01/2021 09:30:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:45:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21365 order_number,to_date('02/01/2021 14:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('02/01/2021 17:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21367 order_number,to_date('01/01/2021 09:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:43:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 16:34:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 18:15:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
)
or
The following image ilustrates the operation I am trying to execute (the date ranges are not the same as the ones I provided earlier)
Could anyone provide a SQL query or PL/SQL program that does that?

It seems what you are looking for is not INTERSECT but overlaps. In Oracle intersect generally refers to the common result of 2 queries:
Select <columns list> from table1
INTERSECT
Select <columns list> from table2;
Where the column lists have the same definition and the resulting values are the same. What you are looking for is where the values overlap one another each other not where the rows contain same values.
Lets consider 2 events call then 'A' and 'B', there are 4 possibilities for overlap:
A starts, B starts, B ends, A ends. A completely overlaps B.
A starts, B starts, A ends, B ends. A overlaps beginning of B
B starts, A starts, B ends, A ends. A overlaps ending of B
B starts, A starts, A ends, B ends. A is completely overlap by B.
Resolving is just determining is needs to determine the overlap we take the greatest start time and the least end time. With the data you provided this requires just one of the above:
select order_number
, greatest(t1start, t2start) start_date_time
, least(t1finish,t2finish) finish_date_time
from ( select t1.order_number
, t1.start_date_time t1start
, t1.finish_date_time t1finish
, t2.start_date_time t2start
, t2.finish_date_time t2finish
from t_result_set_one t1
join t_result_set_two t2
on t1.order_number = t2.order_number
where ( t1.finish_date_time >= t2.start_date_time
and t1.start_date_time <= t2.finish_date_time
)
);
See fiddle here. I leave the other 3 possibilities for you.

Related

Group by singles chute

How can I create an overview without duplicates in column "CHUTE"?
Eg in below result: AX002 = 129
select
COUNT(PPL_SDCC),
substr (PPL_DISCHARGEID,6,5) as CHUTE
from T1LOG.PPL_PIECELOG
where
PPL_DISCHARGEID like 'PS%X%'
and substr (PPL_SDCC,11,1)='1'
and PPL_DISCHARGETIME between TO_DATE ('01/08/2021 10:00:00' , 'DD/MM/YYYY hh24:mi:ss') and TO_DATE ('02/08/2021 09:00:00' , 'DD/MM/YYYY hh24:mi:ss')
group by PPL_DISCHARGEID
order by CHUTE
RESULT:
Rahther than grouping only column PPL_DISCHARGEID, You should actually include the exact calculation in group by clause. Please update your group by clause in your query to -
SELECT COUNT(PPL_SDCC),
SUBSTR(PPL_DISCHARGEID,6,5) as CHUTE
FROM T1LOG.PPL_PIECELOG
WHERE PPL_DISCHARGEID like 'PS%X%'
AND SUBSTR(PPL_SDCC,11,1)='1'
AND PPL_DISCHARGETIME BETWEEN TO_DATE('01/08/2021 10:00:00', 'DD/MM/YYYY hh24:mi:ss') and TO_DATE('02/08/2021 09:00:00', 'DD/MM/YYYY hh24:mi:ss')
GROUP BY SUBSTR(PPL_DISCHARGEID,6,5)
ORDER BY CHUTE;

Union times in sql oracle - difficult problem

I have a big table with start times and end times.
It looks like this:
Start_time date-time (format: dd/mm/yyyy hh24:mi:ss),
End_time date-time (format: dd/mm/yyyy hh24:mi:ss)
I might have rows that represent time which is included in other rows
My desiarable result is a table that can solve this containing. I want take any firsy time and see next to him the last end time.
I tried to left join the table with itself on start time between start time and end time if the end of the second is greater than the ending of the first. Then to do a sliding window and take the max end time with sliding window or even with group by.
However, this idea does take in account then I may have, for example:
10:05-10:10
10:07-10:12
10:09-10:15
10:11-10:20
So when I am joined I allegedly get 10:05-10:15 and 10:11-10:20. The row of 10:11 is not joined to the first row because it is not included in that time.
I have here again the same problem I had in the begining.
My desiarable result is actually for the rows above:
10:05-10:20
Seem to be a difficult problem.
I dont know plsql but thought maybe about doing some function that repeat this query until it has nothing to join?
Hope to get ypur help!
Thanks.
I dont know how to format, but you can copy paste in your editor and than format.
Insted of my test data with operator "with" you may use your table.
I suppose you have some sort of ID so i include it:
with test_table as (
select 1 id, to_date('2019-04-07 10:05', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-07 10:08', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 2 id, to_date('2019-04-07 10:07', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-07 10:10', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 3 id, to_date('2019-04-07 10:11', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-07 10:15', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 4 id, to_date('2019-04-07 10:12', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-07 10:20', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 5 id, to_date('2019-04-08 10:05', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-08 10:10', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 6 id, to_date('2019-04-08 10:07', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-08 10:12', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 7 id, to_date('2019-04-08 10:09', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-08 10:15', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 8 id, to_date('2019-04-08 10:11', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-08 10:20', 'yyyy-mm-dd hh24:mi') end_time from dual
)
select id, to_char(start_time, 'yyyy-mm-dd hh24:mi') start_time, to_char(end_time, 'yyyy-mm-dd hh24:mi') end_time,
(SELECT MAX(to_char(end_time, 'yyyy-mm-dd hh24:mi'))
from test_table t2
connect by nocycle
prior t2.id != t2.id and
PRIOR end_time > start_time and
PRIOR start_time < end_time
start with t2.id = t1.id) max_date
from test_table t1;

Oracle query to get data between specific time

We are trying to find out the transactions made in between 00 to 03 AM for WHOLE YEAR.
Tried extract and to_date functions with many modification but unable to get the exact result.
Below Gives data just for current month even the date is not specified
and TXNDATE >= to_date('00:00:00', 'HH24:MI:SS')
and TXNDATE <= to_date('03:30:00','HH24:MI:SS')
Please assist.
How about just extracting the hour?
and extract(hour from TXNDATE) < 3
If you want to handle the time directly, strings might be the best approach:
to_char(txndate, 'HH24:MI:SS') >= '00:00:00' and
to_char(txndate, 'HH24:MI:SS') < '03:00:00'
If performance is an issue and you want to use an index, you can create an index incorporating to_char(txndate, 'HH24:MI:SS').
Try this:
WHERE to_char(tnxdate, 'HH24:MI:SS') between '00:00:00' and '03:30:00'
You can do this using 'EXTRACT' but you have to be careful about coding the edge conditions correctly. Here's an example:
WITH cteTxndates AS (SELECT TO_DATE('01-JAN-2018 00:00:00', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('01-JAN-2018 00:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('05-FEB-2018 00:01:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('10-MAR-2018 00:10:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('15-MAR-2018 01:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('20-APR-2018 10:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL --
SELECT TO_DATE('25-MAY-2018 02:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('30-JUN-2018 03:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('02-JUL-2018 10:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL --
SELECT TO_DATE('20-AUG-2018 01:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('03-SEP-2018 02:30:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('30-OCT-2018 03:30:00', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('30-OCT-2018 03:30:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL --
SELECT TO_DATE('04-NOV-2018 02:22:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('14-DEC-2018 01:11:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL), -- *
cteDatefields AS (SELECT TXNDATE,
EXTRACT(HOUR FROM CAST(TXNDATE AS TIMESTAMP)) AS XHOUR,
EXTRACT(MINUTE FROM CAST(TXNDATE AS TIMESTAMP)) AS XMINUTE,
EXTRACT(SECOND FROM CAST(TXNDATE AS TIMESTAMP)) AS XSECOND
FROM cteTxndates)
SELECT *
FROM cteDatefields
WHERE XHOUR >= 0 AND
( XHOUR < 3 OR
(XHOUR = 3 AND XMINUTE < 30) OR
(XHOUR = 3 AND XMINUTE = 30 AND XSECOND = 0));
dbfiddle here

How to find a gap between two dates in a same column based on group chnage

I have sample log table as below
I want to find a difference between each "END" and next "Start" TYPE. as below
I have setup a SQLFiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Logs ( id, DateTime, Type ) AS
SELECT 9000, TO_DATE('2018-03-01 09:00:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03/10 10:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-11 11:00:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03/20 10:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03-21 10:05:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03/25 09:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-26 10:15:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03/27 12:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9002, TO_DATE('2018-03-28 14:30:27','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9002, TO_DATE('2018-04-05 15:02:56','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL;
Please guide me how can I achieve this
EDIT 1:
As "wolfrevokcats" perfectly produce the result I mentioned, I have little modification in my Out Result I want to add the GAP Result in Next Start ID like below picture
By Modifying code provided by "wolfrevokcats" I am able to get the result
Full Working Code is below for reference
WITH logs AS (
SELECT 9000 id, TO_DATE('2018-03-01 09:00:00','RRRR-MM-DD HH24:MI:SS') AS datetime, 'Start' AS type FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03/10 10:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-11 11:00:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03/20 10:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03-21 10:05:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03/25 09:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-26 10:15:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03/27 12:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9002, TO_DATE('2018-03-28 14:30:27','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9002, TO_DATE('2018-04-05 15:02:56','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL
)
, t as (
select id, type, lag(type) over(order by datetime) prev_type
, datetime time_end, lag(datetime) over(order by datetime) prev_time_end
from logs l
)
select t.id, t.time_end, t.prev_time_end
, trunc(time_end) - trunc(t.prev_time_end) difference
from t
where nvl(prev_type,'END') = 'END' and type='Start';
Using a simple lead would do:
WITH logs AS (
SELECT 9000 id, TO_DATE('2018-03-01 09:00:00','RRRR-MM-DD HH24:MI:SS') AS datetime, 'Start' AS type FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03-10 10:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-10 11:00:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-20 10:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03-20 10:05:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9000, TO_DATE('2018-03-25 09:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-25 10:15:00','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9001, TO_DATE('2018-03-26 12:00:00','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL UNION ALL
SELECT 9002, TO_DATE('2018-03-26 14:30:27','RRRR-MM-DD HH24:MI:SS'), 'Start' FROM DUAL UNION ALL
SELECT 9002, TO_DATE('2018-04-05 15:02:56','RRRR-MM-DD HH24:MI:SS'), 'END' FROM DUAL
)
, t as (
select id, type, lead(type) over(order by datetime) next_type
, datetime time_end, lead(datetime) over(order by datetime) next_time_start
from logs l
)
select t.id, t.time_end, t.next_time_start
, trunc(t.next_time_start) - trunc(time_end) difference
from t
where nvl(next_type,'Start') = 'Start' and type='END'
ID TIME_END NEXT_TIME_START DIFFERENCE
----- -------------------- -------------------- -----------
9000 2018-03-10 10:00:00 2018-03-10 11:00:00 0
9001 2018-03-20 10:00:00 2018-03-20 10:05:00 0
9000 2018-03-25 09:00:00 2018-03-25 10:15:00 0
9001 2018-03-26 12:00:00 2018-03-26 14:30:27 0
9002 2018-04-05 15:02:56
Note that all the differences in your SQLFiddle data are less that 1 day, so the query shows only zeros.
Assuming that your starts and ends exactly line up (as in your sample data), you can enumerate them and then aggregate:
select id, min(datetime_disp) as start_time, max(datetime_disp) as end_time,
trunc(max(datetime_disp) - min(datetime_disp)) as diff
from (select l.*,
row_number() over (partition by id, type order by datetime_disp) as seqnum
from logs l
) l
group by id, seqnum;
Note that this assumes that the values line up (never two starts in a row, for instance). If this can happen, then ask another question and explain how to handle those situations.
You can use row_number to assign an increasing number to each start and end within the same document. Then you can join start and end together:
with numbered as
(
select id
, datetime
, type
, row_number() over (partition by id, type order by datetime) rn
from Logs
)
select a.id
, b.datetime as TIME_END
, a.datetime as NEXT_TIME_START
, b.datetime - a.datetime as DIFF
from numbered a
left join
numbered b
on a.id = b.id
and a.rn = b.rn -- Match the Nth Start to the Nth END
and b.type = 'END'
where a.type = 'Start'
This method doesn't use the type column at all. It relies on there being a matching END for every Start. It should work for overlapping periods.

Find total duration inside date range that is covered by a list of ranges

I have a bunch of date ranges stored in records in a table, many of which overlap.
2013-03-10 10:00 - 2013-03-10 16:00
2013-03-10 15:00 - 2013-03-10 17:00
2013-03-10 20:00 - 2013-03-10 22:00
For my cursor I have two input dates, and I need to know how much time between these two dates is covered by the ranges stored in the above table.
For this range:
2013-03-10 12:00 - 2013-03-10 23:00
7 of hours are covered by the above ranges
Hopefully that is clear.
Normally I would post what I have tried, but I can't seem to wrangle it into an SQL query at all. The best I have is a PLSQL function call from within my cursor which loops over a couple of other cursors trying to blend the date ranges in the table together and then subtract it from the given range and its a horrible mess.
I am currently using Oracle 10g, but we are currently in the process of migrating to 11g, so I can't use anything that's specific to either version until the migration is complete.
Schema:
create table Overlapping_Ranges (B date, E date);
insert into Overlapping_Ranges (B, E) values
(to_date('2013-03-10 10:00', 'yyyy-mm-dd hh24:mi'), to_date('2013-03-10 16:00', 'yyyy-mm-dd hh24:mi'));
insert into Overlapping_Ranges (B, E) values
(to_date('2013-03-10 15:00', 'yyyy-mm-dd hh24:mi'), to_date('2013-03-10 17:00', 'yyyy-mm-dd hh24:mi'));
insert into Overlapping_Ranges (B, E) values
(to_date('2013-03-10 20:00', 'yyyy-mm-dd hh24:mi'), to_date('2013-03-10 22:00', 'yyyy-mm-dd hh24:mi'));
create table Ranges_from_cursor (B date, E date);
insert into Ranges_from_cursor (B, E) values
(to_date('2013-03-10 12:00', 'yyyy-mm-dd hh24:mi'), to_date('2013-03-10 23:00', 'yyyy-mm-dd hh24:mi'));
Select:
select
to_char(c.B, 'yyyy-mm-dd hh24:mi') as "From",
to_char(c.E, 'yyyy-mm-dd hh24:mi') as "To",
sum(greatest(0, least(r.E, c.E) - greatest(r.B, c.B))) * 24 as "Hours Overlapped"
from
Ranges_from_cursor c,
(
select distinct
r.B, r.E
from
(
select
T as B,
lead(T) over (order by T) as E,
T + (lead(T) over (order by T) - T)/2 as M
from
(
select B as T from Overlapping_Ranges
union
select E as T from Overlapping_Ranges
)
) r
join Overlapping_Ranges o
on r.M between o.B and o.E
)r
group by c.B, c.E
order by 1
Output:
FROM TO HOURS OVERLAPPED
2013-03-10 12:00 2013-03-10 23:00 7
fiddle
is this what you looking for?
SELECT MIND1,
MAXD2,
24*(MAXD2-MIND1)
FROM
(SELECT MIN(d1) MIND1,
MAX(d2)MAXD2
FROM my_test1
WHERE D1>'10-mar-2013 12:00:00'
AND d2 < '10-mar-2013 23:00:00'
);
see the fiddle here
This works perfect for me.
WITH daterange AS (
SELECT TO_DATE('2013-03-10 10:00', 'YYYY:MM:DD HH24:MI') AS date_from, TO_DATE('2013-03-10 16:00', 'YYYY:MM:DD HH24:MI') AS date_to FROM dual
UNION ALL
SELECT TO_DATE('2013-03-10 15:00', 'YYYY:MM:DD HH24:MI'), TO_DATE('2013-03-10 18:00', 'YYYY:MM:DD HH24:MI') FROM dual
UNION ALL
SELECT TO_DATE('2013-03-10 19:00', 'YYYY:MM:DD HH24:MI'), TO_DATE('2013-03-10 22:00', 'YYYY:MM:DD HH24:MI') FROM dual
)
SELECT FLOOR(sum_diff)
||' days, '||(sum_diff - FLOOR(sum_diff))*24||' hours'
AS hours_between
FROM (SELECT SUM(CASE
WHEN date_from >= TO_DATE(:date_from, 'YYYY-MM-DD HH24:MI')
AND date_to <= TO_DATE(:date_to, 'YYYY-MM-DD HH24:MI') THEN
(date_to - date_from)
WHEN date_from >= TO_DATE(:date_from, 'YYYY-MM-DD HH24:MI')
AND date_to > TO_DATE(:date_to, 'YYYY-MM-DD HH24:MI') THEN
(TO_DATE(:date_to, 'YYYY-MM-DD HH24:MI') - date_from)
WHEN date_from < TO_DATE(:date_from, 'YYYY-MM-DD HH24:MI')
AND date_to <= TO_DATE(:date_to, 'YYYY-MM-DD HH24:MI') THEN
(date_to - TO_DATE(:date_from, 'YYYY-MM-DD HH24:MI'))
WHEN date_from < TO_DATE(:date_from, 'YYYY-MM-DD HH24:MI')
AND date_to > TO_DATE(:date_to, 'YYYY-MM-DD HH24:MI') THEN
(TO_DATE(:date_to, 'YYYY-MM-DD HH24:MI') - TO_DATE(:date_from, 'YYYY-MM-DD HH24:MI'))
ELSE 0
END) AS sum_diff -- The differences are numeric values, hence sum will also be numeric.
FROM daterange dr
WHERE date_to > TO_DATE(:date_from, 'YYYY-MM-DD HH24:MI')
AND date_from < TO_DATE(:date_to, 'YYYY-MM-DD HH24:MI'))
;
Note: The WITH clause is used just for illustration purposes. Replace with your own tablename wherever applicable.