oracle19cwith-clause

SQL for nested WITH CLAUSE - RESULTS OFFSET in Oracle 19c


Please suggest a way to implement nesting of (temp - results - select) as shown below?

I see that oracle 19c does not allow nesting of WITH clause.

with temp2 as   
(   
    with temp1 as
    (
        __
        __
    ),
    results(..fields..) as
    (
        select ..<calc part>.. from temp1, results where __
    )   
    select ..<calc part>.. from temp1 join results where __
    
),
results(..fields..) as
(
    select ..<calc part>.. from temp2, results where __
)   
select ..<calc part>.. from temp2 join results where __

For instance:

DB Fiddle

I need to calculate CALC3 in similar recursive way as of CALC


CREATE TABLE TEST ( DT DATE, NAME VARCHAR2(10), VALUE NUMBER(10,3));

insert into TEST values ( to_date( '01-jan-2021'), 'apple', 198.95 );
insert into TEST values ( to_date( '02-jan-2021'), 'apple', 6.15 );
insert into TEST values ( to_date( '03-jan-2021'), 'apple', 4.65 );
insert into TEST values ( to_date( '06-jan-2021'), 'apple', 20.85 );
insert into TEST values ( to_date( '01-jan-2021'), 'banana', 80.5 );
insert into TEST values ( to_date( '02-jan-2021'), 'banana', 9.5 );
insert into TEST values ( to_date( '03-jan-2021'), 'banana', 31.65 );

--Existing working code - 

with t as
 ( select
     test.*,
     row_number() over ( partition by name order by dt ) as seq
   from test
 ),
 results(name, dt, value, calc ,seq) as
 (
   select name, dt, value, value/5 calc, seq
   from t
   where seq = 1
   union all
   select t.name, t.dt, t.value, ( 4 * results.calc + t.value ) / 5, t.seq
   from t, results
   where t.seq - 1 = results.seq
   and  t.name = results.name
 )
 select results.*, calc*3 as calc2  -- Some xyz complex logic as calc2
 from results
 order by name, seq;

Desired output:

CALC3 - grouped by name and dt -

((CALC3 of prev day record * 4) + CALC2 of current record )/ 5

i.e for APPLE 
for 1-jan-21, CALC = ((0*4)+119.37)/5 = 23.87 -------> since it is 1st record, have taken 0 as CALC3 of prev day record
for 2-jan-21, CALC = ((23.87*4)+99.19)/5= 115.33 -----> prev CALC3 is considered from 1-jan-21 - 23.87 and 99.19 from current row
for 3-jan-21, CALC = ((115.33*4)+82.14)/5= 477.76 and so on

For BANANA
1-jan-21, CALC = ((0*4)+48.30)/5=9.66
1-jan-21, CALC = ((9.66*4)+44.34)/5=47.51 
etc

enter image description here


Solution

  • You do not need to, you can just do it all in one level:

    with temp1(...fields...) as
    (
        __
        __
        __
    ),
    results1(...fields...) as
    (
      select ...<calc part>... from temp1 where __
    ),
    temp2( ...fields...) as   
    (   
        select ...<calc part>... from temp1 join results1 where __
    ),
    results2(...fields...) as
    (
        select ...<calc part>... from temp2 where __
    )   
    select ...<calc part>... from temp2 join results2 where __
    

    For your actual problem, you can use a MODEL clause:

    SELECT dt,
           name,
           amount,
           calc,
           seq,
           calc2,
           calc3
    FROM   (
      SELECT t.*,
             ROW_NUMBER() OVER (PARTITION BY name ORDER BY dt) AS seq
      FROM   test t
    )
    MODEL
      PARTITION BY (name)
      DIMENSION BY (seq)
      MEASURES ( dt, amount, 0 AS calc, 0 AS calc2, 0 as calc3)
      RULES (
        calc[1] = amount[1]/5,
        calc[seq>1] = (amount[cv(seq)] + 4*calc[cv(seq)-1])/5,
        calc2[seq] = 3*calc[cv(seq)],
        calc3[1] = calc2[1]/5,
        calc3[seq>1] = (calc2[cv(seq)] + 4*calc3[cv(seq)-1])/5
      )
    

    Which outputs:

    DT NAME AMOUNT CALC SEQ CALC2 CALC3
    01-JAN-21 banana 80.5 16.1 1 48.3 9.66
    02-JAN-21 banana 9.5 14.78 2 44.34 16.596
    03-JAN-21 banana 31.65 18.154 3 54.462 24.1692
    01-JAN-21 apple 198.95 39.79 1 119.37 23.874
    02-JAN-21 apple 6.15 33.062 2 99.186 38.9364
    03-JAN-21 apple 4.65 27.3796 3 82.1388 47.57688
    06-JAN-21 apple 20.85 26.07368 4 78.22104 53.705712

    db<>fiddle here