Home > database >  Evalaute Oracle Table to Calulate or Extend Multiple Date Ranges into One Period
Evalaute Oracle Table to Calulate or Extend Multiple Date Ranges into One Period

Time:08-20

I have a table that shows date ranges of an object. For reference

  • The object needs some sort of event in order to prevent it from expiring.
  • We have a rule where we can apply a protection against the object from expiring, so that there is no need to action the object.
  • The date ranges are a period of time that doesn't allow the object to expire (the protection period)
  • A protection can be applied and removed many times over the life of the object.

An example is the following:

OBJECTID PROTECTION_START_DATE PROTECTION_EXPIRY_DATE
843856 04-DEC-17 31-AUG-18
843856 11-OCT-18 31-OCT-18
843856 16-OCT-18 30-JUN-19
843856 28-MAY-19 31-OCT-19
843856 09-SEP-19 09-SEP-20
843856 09-SEP-19 31-DEC-21
843856 18-OCT-21 30-SEP-22

Sometimes a protection will expire on a date, but we will put an additional protection on the object on the same date the previous protection expired or even before the expiry date of the protection, in order to extend that protection further. What I would like to do is evaluate the table, determine where these situations arise and calculate the complete range of dates where these extended protection occur.

The end result would look something like the table below, where the first row is a unique protection period. When it expired, we didn't add an additional protection on or before the expiry date. The next 6 rows kept extending the next protection, where it was initially applied on 11-OCT-18 (dd-mmm-yy date format), but on or before the expiry date, we kept extending the protection until the expiry date on the last row (30-SEP-22).

OBJECTID PROTECTION_START_DATE PROTECTION_EXPIRY_DATE
843856 04-DEC-17 31-AUG-18
843856 11-OCT-18 30-SEP-22

I have been playing with some cross joins to create some Cartesian tables, but I'm pretty new to that function and I am not sure how to handle this situation. Any suggestions on how to do this is very appreciated!

CodePudding user response:

with 
  -- 1. Mark the start of each new protection period
  period_starts as (
    select 
      p.*,
      case 
        when protection_start_date > lag(protection_expiry_date) over(partition by objectid order by protection_start_date) 
        then 1
        else 0 
      end new_period
    from protections p
  ),
  
  -- 2. From each period start, make up a grouping by running cumulative sum for objectids
  periods as (
    select 
      p.*,
      sum(new_period) over(partition by objectid order by protection_start_date) period_group
    from period_starts p
  )
  
  -- 3. For each period group, calculate min(start) and max(end) date
select 
  objectid,
  min(protection_start_date) protection_start_date,
  max(protection_expiry_date) protection_expiry_date
from periods
group by objectid, period_group
OBJECTID | PROTECTION_START_DATE | PROTECTION_EXPIRY_DATE
-------: | :-------------------- | :---------------------
  843856 | 04-DEC-17             | 31-AUG-18             
  843856 | 11-OCT-18             | 30-SEP-22             

db<>fiddle here

CodePudding user response:

From Oracle 12, you can use MATCH_RECOGNIZE for row-by-row processing:

SELECT *
FROM   table_name
MATCH_RECOGNIZE(
  PARTITION BY objectid
  ORDER     BY protection_start_date
  MEASURES
    FIRST(protection_start_date) AS protection_start_date,
    MAX(protection_expiry_date)  AS protection_expiry_date
  PATTERN (first_date overlapping*)
  DEFINE
    overlapping AS PREV(protection_expiry_date) >= protection_start_date
)

Which, for the sample data:

CREATE TABLE table_name (OBJECTID, PROTECTION_START_DATE, PROTECTION_EXPIRY_DATE) AS
SELECT 843856, DATE '2017-12-04', DATE '2018-08-31' FROM DUAL UNION ALL
SELECT 843856, DATE '2018-10-11', DATE '2018-10-31' FROM DUAL UNION ALL
SELECT 843856, DATE '2018-10-16', DATE '2019-06-30' FROM DUAL UNION ALL
SELECT 843856, DATE '2019-05-28', DATE '2019-10-31' FROM DUAL UNION ALL
SELECT 843856, DATE '2019-09-09', DATE '2020-09-09' FROM DUAL UNION ALL
SELECT 843856, DATE '2019-09-09', DATE '2021-12-31' FROM DUAL UNION ALL
SELECT 843856, DATE '2021-10-18', DATE '2022-09-30' FROM DUAL;

Outputs:

OBJECTID PROTECTION_START_DATE PROTECTION_EXPIRY_DATE
843856 2017-12-04 00:00:00 2018-08-31 00:00:00
843856 2018-10-11 00:00:00 2022-09-30 00:00:00

db<>fiddle here

  • Related