postgresql - 返回 PostgreSQL 中更新值的变化

标签 postgresql audit

假设我有两个表, 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/

相关文章:

postgresql - 从转储文件创建的数据库的 Postgres 权限

postgresql - 如何批量替换 PostgreSQL 数据库模式中的结构?

postgresql - 是否可以直接从数据库表中读取数据以在 Apache Flink 中进行批处理,而不是从 csv 文件中读取数据?

css - 灯塔标记的神秘 CSS 是什么?

python - GraphQL + Django : resolve queries using raw PostgreSQL query

c# - Npgsql 中的 InvalidCastException

java - oracle中如何给触发器发送参数

mysql - 用户作为数据库用户或用户作为用户表、审计表中的一行。

java - 使用 Hibernate Envers 时如何在审核后立即获取 rev id

java - 仅使用 Primefaces 数据表对 JSF 进行操作审核