zukucode
主にWEB関連の情報を技術メモとして発信しています。

PostgreSQLで複数の更新をトランザクションにまとめる

注文を登録するとき、注文本体と明細を別々に保存すると、途中で失敗して片方だけ残る可能性があります。両方を1つの操作として扱いたい場合はトランザクションを使います。

BEGINとCOMMIT

BEGIN;

INSERT INTO orders (order_id, customer_id)
VALUES (1, 10);

INSERT INTO order_items (order_id, product_id, quantity)
VALUES (1, 20, 2);

COMMIT;

BEGINからCOMMITまでの変更を1つにまとめます。例では、ordersorder_itemsに必要な列と参照制約がある前提です。

別の接続からはどう見えるか

接続AでBEGINして注文をINSERTしたあと、まだCOMMITしていなければ、通常は接続Bからその注文は見えません。AがCOMMITすれば見えるようになり、ROLLBACKすれば存在しなかったことになります。トランザクションは更新をまとめるだけでなく、途中の状態を他の処理へ見せない役割も持ちます。

失敗したらROLLBACK

途中のSQLが失敗した場合は、ROLLBACKでトランザクション内の変更を取り消します。

BEGIN;
UPDATE products SET stock = stock - 1 WHERE product_id = 20;
- 後続の処理で失敗したら
ROLLBACK;

PostgreSQLではトランザクション中にSQLエラーが起きると、通常はそのトランザクションが失敗状態になります。続行せず、ROLLBACKしてからやり直します。アプリケーションでは例外時に確実にrollbackまたは接続の破棄が行われるようにします。

在庫を減らす例では、トランザクションを使うだけで在庫が負になることは防げません。WHERE product_id = 20 AND stock > 0のような条件付き更新にして、更新件数が1件かも確認します。複数処理の原子性と、業務条件の検証は別の問題です。

メール送信やHTTP API呼び出しは、DBのROLLBACKでは取り消せません。トランザクション内で扱える範囲を確認してください。


関連記事