[英]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.