EN VI

How to implement recursion using SQL Oracle?

2024-03-14 21:00:05
How to implement recursion using SQL Oracle?

I have a table with the following columns:

ID, date, amount, amount2

Column amount2 has values only in the first row. I need to recursively calculate each row of column amount2 using values from previous row of this column and also column amount

  • second row for amount2 should be: first row value of amount2 - second row value of amount
  • third row for amount2 should be: second row value of amount2 - third row value of amount ...

I tried to use following code, but it does not work:

SELECT   
    first_column,  
    CASE   
        WHEN second_column IS NULL THEN LAG(first_column, 1, first_column) OVER (ORDER BY row_id) - LAG(first_column, 1, first_column) OVER (ORDER BY row_id DESC)  
        ELSE second_column  
    END AS calculated_second_column
from table;

Reproducible example:

CREATE TABLE your_table_name (  
    id INT,  
    date DATE,  
    amount DECIMAL(10,2),  
    amount1 DECIMAL(10,2)  
); 




INSERT INTO your_table_name (id, date, amount, amount1)  
VALUES  
    (234, '2020-01-01', 4, 10),  
    (234, '2020-01-02', 7, NULL),  
    (234, '2020-01-03', 10, NULL),  
    (234, '2020-01-04', 15, NULL),  
    (234, '2020-01-05', 6, NULL);  

I expect to have in column amount1; 10, 10-7=3, 3-10=-7, -7-15=-22, -22-6=-28

Solution:

Find first row using the FIRST_VALUE analytic function and then use the SUM analytic function to find a cumulative total and subtract:

SELECT  id,
        dt,
        amount,
        -- Find the first amount + amount1 value
        FIRST_VALUE(amount + amount1) OVER (PARTITION BY id ORDER BY dt)
        -- Subtract the cumulative sum of the amount values
        - SUM(amount) OVER (PARTITION BY id ORDER BY dt)
        AS amount1
FROM    your_table_name;

Or, using COALESCE in the cumulative SUM:

SELECT  id,
        dt,
        amount,
        SUM(COALESCE(amount1, -amount)) OVER (PARTITION BY id ORDER BY dt)
          AS amount1
FROM    your_table_name;

Or, using a MODEL clause:

SELECT id, dt, amount, amount1
FROM (
  SELECT t.*, ROW_NUMBER() OVER (PARTITION BY id ORDER BY dt) AS rn
  FROM   your_table_name t
)
MODEL
  PARTITION BY (id)
  DIMENSION BY (rn)
  MEASURES (dt, amount, amount1)
  RULES (
    amount1[rn>1] = amount1[cv(rn)-1] - amount[cv(rn)]
  );

Which, for the sample data:

CREATE TABLE your_table_name (
  id      INT,
  dt      DATE,
  amount  DECIMAL(10,2),
  amount1 DECIMAL(10,2)
);

INSERT INTO your_table_name (id, dt, amount, amount1)
VALUES
(234, DATE '2020-01-01',  4,   10),
(234, DATE '2020-01-02',  7, NULL),
(234, DATE '2020-01-03', 10, NULL),
(234, DATE '2020-01-04', 15, NULL),
(234, DATE '2020-01-05',  6, NULL);

All output:

ID DT AMOUNT AMOUNT1
234 2020-01-01 00:00:00 4 10
234 2020-01-02 00:00:00 7 3
234 2020-01-03 00:00:00 10 -7
234 2020-01-04 00:00:00 15 -22
234 2020-01-05 00:00:00 6 -28

fiddle

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login