Create the Table
create table meeting_attendees (
attendee_id integer
not null,
start_date date
not null,
end_date date,
primary key (
attendee_id, start_date
)
)
Table created.
Load the Data
begin
insert into meeting_attendees
values ( 1, date'2020-06-17' + 9/24, date'2020-06-17' + 10/24 );
insert into meeting_attendees
values ( 1, date'2020-06-17' + 10/24, date'2020-06-17' + 10.5/24 );
insert into meeting_attendees
values ( 1, date'2020-06-17' + 11/24, date'2020-06-17' + 13/24 );
insert into meeting_attendees
values ( 1, date'2020-06-17' + 16/24, date'2020-06-17' + 17/24 );
insert into meeting_attendees
values ( 2, date'2020-06-17' + 9/24, date'2020-06-17' + 12/24 );
insert into meeting_attendees
values ( 2, date'2020-06-17' + 12.5/24, date'2020-06-17' + 13/24 );
insert into meeting_attendees
values ( 2, date'2020-06-17' + 13/24, date'2020-06-17' + 14/24 );
insert into meeting_attendees
values ( 2, date'2020-06-17' + 15/24, date'2020-06-17' + 17/24 );
end;
Statement processed.
alter session set nls_date_format = ' DD Mon YY HH24:MI '
Statement processed.
select * from meeting_attendees
order by start_date, end_date
ATTENDEE_ID | START_DATE | END_DATE | 1 | 17 Jun 20 09:00 | 17 Jun 20 10:00 | 2 | 17 Jun 20 09:00 | 17 Jun 20 12:00 | 1 | 17 Jun 20 10:00 | 17 Jun 20 10:30 | 1 | 17 Jun 20 11:00 | 17 Jun 20 13:00 | 2 | 17 Jun 20 12:30 | 17 Jun 20 13:00 | 2 | 17 Jun 20 13:00 | 17 Jun 20 14:00 | 2 | 17 Jun 20 15:00 | 17 Jun 20 17:00 | 1 | 17 Jun 20 16:00 | 17 Jun 20 17:00 |
---|
alter session set nls_date_format = ' HH24:MI '
Statement processed.
Find the Gaps
select *
from meeting_attendees match_recognize (
order by start_date, end_date
measures
max ( end_date ) start_gap,
next ( start_date ) end_gap,
classifier() as cls
all rows per match
pattern ( ( gap | {-no_gap-} )+ )
define gap as max ( end_date ) < next ( start_date )
)
START_DATE | END_DATE | START_GAP | END_GAP | CLS | ATTENDEE_ID | 13:00 | 14:00 | 14:00 | 15:00 | GAP | 2 |
---|
Show the All the Rows
select cls, end_date, start_gap, end_gap
from meeting_attendees match_recognize (
order by start_date, end_date
measures
max ( end_date ) start_gap,
next ( start_date ) end_gap,
classifier() as cls
all rows per match
pattern ( ( gap | no_gap )+ )
define gap as max ( end_date ) < next ( start_date )
)
CLS | END_DATE | START_GAP | END_GAP | NO_GAP | 10:00 | 10:00 | 09:00 | NO_GAP | 12:00 | 12:00 | 10:00 | NO_GAP | 10:30 | 12:00 | 11:00 | NO_GAP | 13:00 | 13:00 | 12:30 | NO_GAP | 13:00 | 13:00 | 13:00 | GAP | 14:00 | 14:00 | 15:00 | NO_GAP | 17:00 | 17:00 | 16:00 | NO_GAP | 17:00 | 17:00 | - |
---|
Make it Reusable with SQL Macros
create or replace function find_gaps (
tab dbms_tf.table_t,
date_cols dbms_tf.columns_t
)
return varchar2
sql_macro
as
begin
return 'find_gaps.tab match_recognize (
order by ' || find_gaps.date_cols ( 1 ) || ', ' || find_gaps.date_cols ( 2 ) || '
measures
max ( ' || find_gaps.date_cols ( 2 ) || ' ) start_gap,
next ( ' || find_gaps.date_cols ( 1 ) || ' ) end_gap
all rows per match
pattern ( ( gap | {-no_gap-} )+ )
define
gap as max ( ' || find_gaps.date_cols ( 2 ) || ' ) < (
next ( ' || find_gaps.date_cols ( 1 ) || ' )
)
)';
end find_gaps;
Function created.
Calling a SQL Macro
select start_gap, end_gap
from find_gaps (
meeting_attendees,
columns ( start_date, end_date )
)
START_GAP | END_GAP | 14:00 | 15:00 |
---|
View the Final SQL Query
declare
l_clob clob;
begin
dbms_utility.expand_sql_text (
input_sql_text => q'!select *
from find_gaps (
meeting_attendees,
columns ( start_date, end_date )
)!',
output_sql_text => l_clob );
dbms_output.put_line(l_clob);
end;
Statement processed.
SELECT "A1"."START_DATE" "START_DATE","A1"."END_DATE" "END_DATE","A1"."START_GAP" "START_GAP","A1"."END_GAP" "END_GAP","A1"."ATTENDEE_ID" "ATTENDEE_ID" FROM (SELECT "A3"."START_DATE" "START_DATE","A3"."END_DATE" "END_DATE","A3"."START_GAP" "START_GAP","A3"."END_GAP" "END_GAP","A3"."ATTENDEE_ID" "ATTENDEE_ID" FROM (SELECT * FROM (SELECT "A2"."ATTENDEE_ID" "ATTENDEE_ID","A2"."START_DATE" "START_DATE","A2"."END_DATE" "END_DATE" FROM "SQL_XYFAXMHMJFABNKUWUHJFVDJLY"."MEETING_ATTENDEES" "A2") "A4" MATCH_RECOGNIZE ( ORDER BY "START_DATE","END_DATE" MEASURES MAX("END_DATE") AS "START_GAP",NEXT("START_DATE") AS "END_GAP" ALL ROWS PER MATCH SHOW EMPTY MATCHES AFTER MATCH SKIP PAST LAST ROW PATTERN ( (("GAP" | {- "NO_GAP" -} ))+) DEFINE "GAP" AS MAX("END_DATE")