I have the following table:
| ID | Transaction | Amount | Inventory | Price |
| ------------ | ------------ | ------------ | ------------ | ------------ |
| 1 | NULL | NULL | 11 | NULL |
| 2 | Sale | -1 | 10 | 100 |
| 3 | Purchase | 2 | 12 | 102 |
| 4 | Sale | -2 | 10 | 103 |
The first line is the starting amount and the following three lines show transactions that change the inventory. I need to calculate a new running average price based on purchases and copy the current average price in case of sales based on the following formula:
If Transaction = NULL (i.e. starting line) then Average = 90;
If Transaction = 'Sale' then Average = lag(Average) (i.e. whatever is the latest calculated average);
If transaction = 'Purchase' then ((Inventory - Amount) * lag(Average)
+ Amount * Price)
/ Inventory
Sorting order is the ID column ascending.
The problem results from the lag(average), as every calculation step requires the previous row to be the updated value, i.e. the calculation must run and update row by row.
The result table should look like this:
| ID | Transaction | Amount | Inventory | Price | Average |
| ------------ | ------------ | ------------ | ------------ | ------------ | ------------ |
| 1 | NULL | NULL | 11 | NULL | 90 |
| 2 | Sale | -1 | 10 | 100 | 90 |
| 3 | Purchase | 2 | 12 | 102 | 92 |
| 4 | Sale | -2 | 10 | 103 | 92 |
The calculation :
ID 1 --> 90 (starting value)
ID 2 --> 90 (copy previous average)
ID 3 --> 92 = ((12 - 2) * 90 + (2 * 102)) / 12
ID 4 --> 92 (copy previous average)
I have tried the following:
- Use a column (Average) with the start value 90 and run the calculation in another column (Average_f).
Select *,
case when [transaction] is null then Average
when [transaction] = 'Sale' then lag(Average) over (order by ID)
when [transaction] = 'Purchase'
then (((Inventory - Amount) * lag(Average) over (order by ID))
+ (Amount * Price)) / Inventory
end as Average_f
from table
Didn't work out:
ID | Transaction | Amount | Inventory | Price | Average | Average_f |
---|---|---|---|---|---|---|
1 | NULL | NULL | 11 | NULL | 90 | 90 |
2 | Sale | -1 | 10 | 100 | NULL | 90 |
3 | Purchase | 2 | 12 | 102 | NULL | NULL |
4 | Sale | -2 | 10 | 103 | NULL | NULL |
- I have also tried with an update statement:
update table
set average = case when [transaction] is null then Average
when [transaction] = 'Purchase'
then (((Inventory - Amount) * (select lag(Average) over (order by ID)
from table t
where t.ID = table.ID))
+ (Amount * Price)) / Inventory
when [transaction] = 'Sale' then (select lag(Average) over (order by ID)
from table t
where t.ID = table.ID)
end
Didn't work either:
ID | Transaction | Amount | Inventory | Price | Average |
---|---|---|---|---|---|
1 | NULL | NULL | 11 | NULL | 90 |
2 | Sale | -1 | 10 | 100 | NULL |
3 | Purchase | 2 | 12 | 102 | NULL |
4 | Sale | -2 | 10 | 103 | NULL |
Is there a way in SQL to calculate each row individually or any other way to calculate the average using the previous average?
2条答案
按热度按时间hujrc8aj1#
The set-based solution that takes into account nonsequential identifiers and removes the restriction on recursion levels.
db<>fiddle
The cursor-based solution for comparison with a set-based solution.
https://dbfiddle.uk/m3nePu26
The result of both solutions:
wmomyfyw2#
If it's recursive calculation, why not implementing it ?