Предположим, у меня есть две таблицы: payments
и payment_events
, где payments
содержит сумму, а payment_events
содержит журнал изменений суммы.
Я получил запрос на изменениесумма на 50
.Теперь я хочу отследить и событие, и сам платеж:
UPDATE payments SET amount = amount + 50 WHERE id = 1234
INSERT INTO payment_events (payment_id, changed_amount) VALUES (payment_id, 50);
Пока это легко, но что, если есть дополнительное требование к сумме, например, есть столбец max_amount
и сумма не должна превышать это значение.
UPDATE payments SET amount = LEAST(max_amount, amount + 50) WHERE id = 1234;
INSERT INTO payment_events (payment_id, changed_amount) VALUES (payment_id, 50);
Внезапно вставка в payment_events
может быть неправильной.Если сумма равна 180
, и мы попытались добавить 50
, тогда мы должны изменить только сумму 20
, поскольку максимальное значение равно 200
.Конечно, я мог бы вернуть новую сумму:
UPDATE payments SET amount = LEAST(max_amount, amount + 50)
WHERE id = 1234
RETURNING amount;
Учитывая, что я знаю предыдущую сумму, я могу просто их развести.Но поскольку эта операция не является атомарной, она подвержена условиям гонки.
Поскольку, насколько я могу судить, RETURNING
может возвращать только фактические столбцы, я не могу просто использовать это для возврата diff.
Пока что единственное решение, которое я нашел, - это добавить бесполезный столбец с именем previous_amount
к payments
и сделать что-то вроде этого:
UPDATE payments SET
previous_amount = amount,
amount = LEAST(max_amount, amount + 50)
WHERE id = 1234
RETURNING previous_amount, amount;
Но это кажетсявроде тупойЕсть ли лучший способ сделать это?
Я делаю это как часть приложения, поэтому запросы выполняются из приложения Ruby / Sequel.