[英]How to conditional update SQL tables?
我有两个这样的数据库表:
CREATE TABLE IF NOT EXISTS accounts (
id INT PRIMARY KEY NOT NULL,
balance INT NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS transactions (
from INT FOREIGN KEY REFERENCES accounts(id),
to INT FOREIGN KEY REFERENCES accounts(id),
amount INT NOT NULL
)
在我的应用程序逻辑中,每当有新的交易请求进来时,我想首先检查 from 账户是否有足够的余额,然后我想从 from 账户中减去金额,将金额添加到 to 账户,然后添加一个新行到事务表。
但是,我想以原子方式完成所有这些工作,原因有两个:
是否可以在单个 SQL 语句中执行此操作? 假设我们正在使用 Postgres。
例如,我知道我可以执行UPDATE accounts SET balance = balance - 6 WHERE id = 1 and balance > 6;
检查帐户是否有足够的余额并同时更新余额。
我还听说过一些名为select... for update
,但我不确定这是否可以帮助我。
另外,查询的结果应该表明交易是否成功,如果没有,我可以向客户端显示一些错误消息吗?
解决方案不是将所有内容都塞进一个 SQL 语句中。 除了复杂之外,它不一定会保护您免受竞争条件的影响。
使用数据库事务。 为避免异常,请使用SELECT... FOR UPDATE
以防止并发修改,或者使用REPEATABLE READ
隔离级别并在遇到序列化错误时重复事务。
为了保护自己免受死锁,请确保始终先锁定具有较低id
的帐户。
这是一个伪代码示例:
EXEC SQL START TRANSACTION;
if (id1 < id2) {
EXEC SQL SELECT balance INTO :bal1 FROM accounts
WHERE id = :id1 FOR UPDATE;
EXEC SQL SELECT balance INTO :bal2 FROM accounts
WHERE id = :id2 FOR UPDATE;
} else {
EXEC SQL SELECT balance INTO :bal2 FROM accounts
WHERE id = :id2 FOR UPDATE;
EXEC SQL SELECT balance INTO :bal1 FROM accounts
WHERE id = :id1 FOR UPDATE;
}
if (bal1 < amount) {
EXEC SQL ROLLBACK;
throw_error('too little money on account');
}
EXEC SQL UPDATE accounts SET balance = :bal1 - :amount WHERE id = :id1;
EXEC SQL UPDATE accounts SET balance = :bal1 + :amount WHERE id = :id2;
EXEC SQL INSERT INTO transaction ("from", "to", amount)
VALUES (:id1, :id2, :amount);
EXEC SQL COMMIT;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.