Home > Software engineering >  Rolling 90 day date window in SQL Oracle
Rolling 90 day date window in SQL Oracle

Time:09-23

SELECT *
  FROM (SELECT date'2021-05-06' AS tran_dt FROM dual UNION
        SELECT date'2021-05-24' FROM dual UNION
        SELECT date'2021-06-25' FROM dual UNION
        SELECT date'2021-07-02' FROM dual UNION
        SELECT date'2021-07-27' FROM dual UNION
        SELECT date'2021-08-16' FROM dual UNION
        SELECT date'2021-08-23' FROM dual UNION
        SELECT date'2021-10-01' FROM dual UNION
        SELECT date'2021-12-31' FROM dual)

Hello

I am working with a date field in SQL Oracle.

I am trying to create rolling window that returns the first row and returns another row when the date is >90 since the last returned row.

In the below example, I want to calculate and return where the Alert field = Yes.

Grateful if anyone has any ideas.

Thanks

Shee7

TRAN_DT    Alert    Days Since Last Alert
06/05/2021  Yes     0
24/05/2021  No      18
25/06/2021  No      50
02/07/2021  No      57
27/07/2021  No      82
16/08/2021  Yes     102
23/08/2021  No      7
01/10/2021  No      46
31/12/2021  Yes     137

CodePudding user response:

You can use a recursion

with firstr as (
  -- a row to start with
  select TRAN_DT
  from (
    select t.*
     , row_number() over(order by TRAN_DT) rn
    from trans t
  )
  where rn=1
),
cte(TRAN_DT) as (
  select * 
  from firstr
  union all
  select t.*
  from cte c
  join trans t on t.TRAN_DT >= c.TRAN_DT   90 and not exists (
               select 1 
               from trans t2 
               where t2.TRAN_DT >= c.TRAN_DT   90 and t2.TRAN_DT < t.TRAN_DT)
)
select * 
from cte;

CodePudding user response:

Maybe there will be a third answer

with tab1 as (
SELECT TRAN_DT
  FROM (SELECT TO_DATE('20210506','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210524','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210625','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210702','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210727','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210816','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210823','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20211001','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20211231','YYYYMMDD') TRAN_DT FROM DUAL)
)
, tab2 as (
select t1.tran_dt dt,
       row_number() over(order by t1.tran_dt) rn
  from tab1 t1
)
, t0(dt, rn, temp) as (
select t1.dt, rn, t1.dt from tab2 t1 where t1.rn = 1
union all
select t2.dt, 
       t2.rn, 
       case when t2.dt - t0.temp > 90 then t2.dt else t0.temp end 
  from tab2 t2, t0 
 where t0.rn   1 = t2.rn
)
select t1.dt, 
       case when t1.dt = t1.temp then 'yes' else 'no' end, 
       nvl(t1.dt - lag(t1.temp) over(order by t1.rn), 0)
  from t0 t1

The third answer, maybe there will be a fourth one

with tab1 as (
SELECT TRAN_DT
  FROM (SELECT TO_DATE('20210506','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210524','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210625','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210702','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210727','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210816','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20210823','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20211001','YYYYMMDD') TRAN_DT FROM DUAL UNION
        SELECT TO_DATE('20211231','YYYYMMDD') TRAN_DT FROM DUAL)
)
, tab2 as (
select t1.tran_dt dt
  from tab1 t1
 order by t1.tran_dt
 fetch next 1 row only
)
, t0(dt) as (
select t1.dt from tab2 t1
union all
select (select min(v1.TRAN_DT) from tab1 v1 where t1.dt   90 < v1.TRAN_DT)
  from t0 t1
 where t1.dt is not null
)
select t1.dt
  from t0 t1
 where t1.dt is not null

CodePudding user response:

From Oracle 12, this is the sort of query that MATCH_RECOGNIZE is intended for:

SELECT tran_dt,
       alert,
       tran_dt
         - LAG(CASE alert WHEN 'Yes' THEN tran_dt END, 1, tran_dt)
             IGNORE NULLS OVER (ORDER BY tran_dt)
         AS days
FROM   table_name
MATCH_RECOGNIZE (
  ORDER BY tran_dt
  MEASURES
    CLASSIFIER() AS alert
  ALL ROWS PER MATCH
  PATTERN ( "Yes" "No"* )
  DEFINE
    "No" AS tran_dt <= "Yes".tran_dt   INTERVAL '90' DAY
)

Before Oracle 12, you can use a recursive query:

WITH dates (tran_dt, rn) AS (
  SELECT tran_dt,
         ROW_NUMBER() OVER (ORDER BY tran_dt) AS rn
  FROM   table_name
),
rolling_dates (tran_dt, alert, rn, days, last_alert) AS (
  SELECT tran_dt, 'Yes', rn, 0, tran_dt
  FROM   dates
  WHERE  rn = 1
UNION ALL
  SELECT d.tran_dt,
         CASE
         WHEN d.tran_dt <= r.last_alert   INTERVAL '90' DAY
         THEN 'No'
         ELSE 'Yes'
         END,
         d.rn,
         d.tran_dt - r.last_alert,
         CASE
         WHEN d.tran_dt <= r.last_alert   INTERVAL '90' DAY
         THEN r.last_alert
         ELSE d.tran_dt
         END
  FROM   rolling_dates r
         INNER JOIN dates d
         ON (r.rn   1 = d.rn)
)
SELECT tran_dt,
       alert,
       days
FROM   rolling_dates;

Both of which outputs:

TRAN_DT ALERT DAYS
2021-05-06 00:00:00 Yes 0
2021-05-24 00:00:00 No 18
2021-06-25 00:00:00 No 50
2021-07-02 00:00:00 No 57
2021-07-27 00:00:00 No 82
2021-08-16 00:00:00 Yes 102
2021-08-23 00:00:00 No 7
2021-10-01 00:00:00 No 46
2021-12-31 00:00:00 Yes 137

db<>fiddle here

  • Related