In a banking DB we want to hold the transactions and remainder amount of accounts after each transaction in a table named 'Operation' like this:
CREATE TABLE Operation (
Id int PRIMARY KEY,
Change int,
PreviousId int null FOREIGN KEY REFERENCES Operation(id),
PreviousAmount int null,
NewAmount AS (COALESCE(PreviousAmount,0) + Change) PERSISTED
);
(for simplicity I removed some columns like AccountId and Date)
PreviousId holds the id of last transaction of this account and will form a chain of transactions.
With this data:
INSERT INTO Operation (Id, Change, PreviousId, PreviousAmount)
VALUES
(1, 1, null, null),
(2, 2, 1, 1),
(3, 3, 2, 3),
(4, 4, 3, 5);
So far PreviousAmount and NewAmount has valid data:
id change previousId previousAmount newAmount
-------------------------------------------------
1 1 NULL NULL 1
2 2 1 1 3
3 3 2 3 6
4 4 3 6 10
let's say we update first row "change":
Update Operation set Change=10 where id=1
I want a db level solution (like trigger) to update all PreviousAmount in the chain of rows so each row holds the updated "NewAmount" of previous row within its "PreviousAmount"
[Edit]
There is a solution to avoid computed column and compute NewAmount using window function in a trigger, but the reason I choose computed column is the reliability of validating non-negative newAmount.
using a window function requires that first an update take place and then another query update the newAmount column and see: if it's negative, rollback the transaction. but using a computed column and a db constraint there is no need for the second query and a transaction rollback as the db engine will do the magic.
--- I will leave this up for now, not it appears from continued discussion that we are not actually dealing with a linked list, so the below may not be an appropriate solution. ---
If after considering the advice from all of the comments above, you still prefer to store the running sums, this can be done with the help of a recursive Common Table Expression (CTE).
Given the id of the inserted or updated row, the CTE can follow the links down the chain and calculate the running totals along the way. This can then be fed into an
UPDATEstatement that applies the changes.For repeated use, this could be wrapped up in a stored procedure. The following procedure will accept the id of the updated record or a NULL id to indicate that all values should be recalculated from the root (rows with previousId = NULL).
It is worth noting that the above recursive CTE does not have any logic to detect cyclic (infinite loop) link arrangements and will error out if the number of recursions exceeds 100 (controlled by the
MAXRECURSIONoption).Use case 1 - Initialization:
Results:
Use case 2 - Updated row:
Results:
Use case 3 - Inserted row:
Results:
See this db<>fiddle for a demonstration of the above.