I have the following table:
InStores
+--------+---------+-----+
| ProdID | StoreID | qty |
+--------+---------+-----+
| p1 | s1 | 25 |
| p1 | s2 | 70 |
+--------+---------+-----+
and two transactions, T1 and T2.
First comes T1:
declare s number(10);
begin
SELECT SUM(qty) into s
FROM instores
WHERE prodID = 'p1';
if s > 90 then
update instores
set qty = qty - 30
where prodid = 'p1'
and storeid = 's2';
end if;
end;
T1 sees the sum of 95 and substracts 30 from p1, s2 (but doesn't commit yet).
T2 makes exactly the same:
declare s number(10);
begin
SELECT SUM(qty) into s
FROM instores
WHERE prodID = 'p1';
if s > 90 then
update instores
set qty = qty - 30
where prodid = 'p1'
and storeid = 's2';
end if;
end;
which also sees the sum of 95, as T1 hasn't commited yet and also doesn't update yet, as there is a Write Lock from T1.
Now T1 commits and T2 is now able to continue and substracts the value of 30 from p1, s2.
We end up having the value 10 for p1, s2 instead of the expected value of 40, as T2 should have seen the sum of 65 (because of the first update (substraction) from T1) and shouldn't have made that second subtraction.
I know that Oracle SQL works with Snapshot Isolation, which forces all transactions to work with the latest commited values, but how can this problem be fixed? I guess that the solution would be that when T1 is active it should prevent all other transactions from starting, but I don't exactly know how I can achieve that. What can I do?
SELECT
....FOR UPDATE
SELECT
+UPDATE
) into a single one? Try to put the select into where clause of the update.