假设我有两个表, payments
和 payment_events
,其中 payment
包含金额, 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
只能返回实际列,因此我不能简单地使用它来返回差异。
到目前为止,我想出的唯一解决方案是将一个可能名为 previous_amount
的无用列添加到 payment
中,并执行如下操作:
UPDATE payments SET
previous_amount = amount,
amount = LEAST(max_amount, amount + 50)
WHERE id = 1234
RETURNING previous_amount, amount;
但这似乎有点愚蠢。有更好的方法吗?
我将其作为应用程序的一部分执行,因此查询是从 Ruby/Sequel 应用程序执行的。
最佳答案
这是触发器的一个很好的应用:
CREATE FUNCTION after_update_trig() RETURNS trigger
LANGUAGE plpgsql AS
$$BEGIN
INSERT INTO payment_events (payment_id, changed_amount)
VALUES (NEW.payment_id, NEW.amount - OLD.amount);
RETURN NEW;
END;$$;
CREATE TRIGGER after_update_trig
AFTER UPDATE ON payments FOR EACH ROW
EXECUTE PROCEDURE after_update_trig();
如果这对您来说不可行,您必须首先获取旧值:
BEGIN;
-- FOR UPDATE prevents concurrent modifications by others
SELECT * FROM payments WHERE id = 1234 FOR UPDATE;
UPDATE payments ... RETURNING *;
INSERT INTO payment_events ...;
COMMIT;
关于postgresql - 返回 PostgreSQL 中更新值的变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54534715/