Reputation: 5
Trying to calculate the column's value wherein the value is getting calculated based on previous row's calculated value. (As in the image below )
which has column Z which is a calculated column as a product of X*Y, case when there is no previous row the value of Y column should be 1 else the value should be previous Z column value.
This can be achieved using Cursor's but trying to find it using SQL Query.
Upvotes: 0
Views: 3728
Reputation: 23588
It looks like what you're trying to do is do a cumulative multiplication across all the rows.
You can do this by using a mathematical trick (aka natural logs and exponentials), where you do a cumulative sum of the natural logs of the x values, and then convert the results back to integers using the exponential function:
WITH your_table AS (SELECT 'ABC' a, 12 x FROM dual UNION ALL
SELECT 'BBC' a, 20 x FROM dual UNION ALL
SELECT 'CBC' a, 10 x FROM dual UNION ALL
SELECT 'XYZ' a, 5 x FROM dual)
SELECT a,
x,
LAG(z, 1, 1) OVER (ORDER BY a) y,
z
FROM (SELECT a,
x,
EXP(SUM(LN(x)) OVER (ORDER BY a)) z
FROM your_table);
A X Y Z
--- ---------- ---------- ----------
ABC 12 1 12
BBC 20 12 240
CBC 10 240 2400
XYZ 5 2400 12000
Then, if you need to see the previous value (aka your y column), you can throw a lag() around the calculated column to find the previous row's value (and if there is no previous row, assign a 1 to it).
ETA: If you already have a table with the information in it, adding a new row becomes a matter of finding the last row and using that to multiply with the new x value. E.g.:
insert into your_table (a, x, y, z)
select 'YMX' a, 2 x, z as new_y, 2*z as new_z
from (select z,
row_number() over (order by a desc) rn
from your_table)
where rn = 1;
This finds the latest row (we use the row_number() analytic function to label the rows, starting with the latest a value (which belongs to the last row)), and we can then use the z value from that row to find the new y value, and multiply it by the new x value to find the new z value.
Then you just need to insert this row.
Upvotes: 3
Reputation: 1270443
I think the simplest method doesn't use lag()
or a subquery at all. One method is simple division:
SELECT a, x, y
( EXP(SUM(LN(x * y)) OVER (ORDER BY a)) ) / (x * y) as z
FROM t;
Or use a window clause with coalesce()
:
SELECT a, x, y
COALESCE( EXP(SUM(LN(x * y)) OVER (ORDER BY a ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)), 1 ) as z
FROM t;
Upvotes: 0