Aggregate date ranges without including overlapping dates
Solution 1:
You can use:
WITH days ( asset_id, loan_start, day_end, loan_end) AS (
SELECT asset_id,
loan_start,
LEAST(TRUNC(loan_start) + INTERVAL '1' DAY, loan_end),
loan_end
FROM table_name
MATCH_RECOGNIZE (
PARTITION BY asset_id
ORDER BY loan_start, loan_end
MEASURES
FIRST(loan_start) AS loan_start,
MAX(loan_end) AS loan_end
ONE ROW PER MATCH
PATTERN (overlaps* next_time)
DEFINE
overlaps AS MAX(overlaps.loan_end) >= loan_start,
next_time AS MAX(overlaps.loan_end) >= loan_start
OR MAX(overlaps.loan_end) IS NULL
)
UNION ALL
SELECT asset_id,
day_end,
LEAST(TRUNC(day_end) + INTERVAL '1' DAY, loan_end),
loan_end
FROM days
WHERE day_end < loan_end
)
SEARCH DEPTH FIRST BY asset_id, loan_start SET order_id
SELECT asset_id,
TO_CHAR(TRUNC(loan_start), 'YYYY-MM-DD') AS day,
SUM(day_end - loan_start) AS amt_unavailable
FROM days
GROUP BY
asset_id,
TRUNC(loan_start)
Which, for the sample data:
CREATE TABLE table_name (ASSET_ID, LOAN_START, LOAN_END) AS
SELECT '00001', DATE '2022-01-01' + INTERVAL '14:00' HOUR TO MINUTE, DATE '2022-01-03' + INTERVAL '08:00' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT '00002', DATE '2022-01-15' + INTERVAL '08:30' HOUR TO MINUTE, DATE '2022-01-15' + INTERVAL '10:00' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT '00002', DATE '2022-01-15' + INTERVAL '09:30' HOUR TO MINUTE, DATE '2022-01-15' + INTERVAL '10:30' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT '00002', DATE '2022-01-15' + INTERVAL '12:00' HOUR TO MINUTE, DATE '2022-01-15' + INTERVAL '18:00' HOUR TO MINUTE FROM DUAL;
Outputs:
ASSET_ID DAY AMT_UNAVAILABLE 00001 2022-01-01 .4166666666666666666666666666666666666667 00001 2022-01-02 1 00001 2022-01-03 .3333333333333333333333333333333333333333 00002 2022-01-15 .3333333333333333333333333333333333333333
db<>fiddle here