CodeGym /コース /SQL SELF /実際のシナリオでのトランザクションの使い方例

実際のシナリオでのトランザクションの使い方例

SQL SELF
レベル 39 , レッスン 3
使用可能

時々、トランザクションってヒーロー映画のキャラみたいだよね。障害やエラー、トラブルからデータベースを救ってくれる。もし複数の操作を分けずにまとめてやらなきゃいけない時、トランザクションが全部まとめてやってくれる。じゃあ、支払い処理の例でどう動くか見てみよう。

支払い処理

よくあるシチュエーションを想像してみて:2つの銀行口座があって、片方からもう片方にお金を送金したい。これは「ワンクリック」な操作じゃないよね。ちゃんと片方からお金を引いて、もう片方に足す必要がある。どこかでミスったら大惨事:両方の口座がそのままだったり、バランスが崩れたり(例えば、お金が消えたり、突然「空から」現れたり)。

シナリオ:口座間の送金

これがサンプルコードだよ。遠い銀河からのメッセージみたいにじっくり読んでみて:

-- トランザクション開始
BEGIN;

-- ステップ1. 送金元アカウントからお金を引く
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;

-- ステップ2. 受取人アカウントにお金を足す
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;

-- 全部うまくいった?じゃあ変更を保存!
COMMIT;

ここで大事なのは?

  • ステップ1ステップ2で何か問題が起きたら(例えばクエリのエラーや残高不足)、トランザクションはロールバックできて、データは元のままになるよ。
  • COMMITは、全部のステップが成功した時だけ変更を確定してくれる。

残高チェックを追加しよう

もし送金元の残高が足りなかったら?マイナスにならないように残高チェックを追加しよう。

-- トランザクション開始
BEGIN;

-- 送金元の現在の残高を取得
DO $$
DECLARE
    current_balance NUMERIC;
BEGIN
    SELECT balance INTO current_balance FROM accounts WHERE account_id = 1;

    -- お金が足りるかチェック
    IF current_balance >= 100 THEN
        -- 足りてたら送金実行
        UPDATE accounts
        SET balance = balance - 100
        WHERE account_id = 1;

        UPDATE accounts
        SET balance = balance + 100
        WHERE account_id = 2;

        -- 変更を確定
        COMMIT;
    ELSE
        -- 足りなかったらロールバック
        ROLLBACK;
        RAISE NOTICE '送金するには残高が足りません!';
    END IF;
END $$;

ここがちょっと面白いポイント!

  • PL/pgSQLブロックでIF条件チェックを使ってる。残高が必要額より少なかったら、トランザクションは却下されて何も変わらない。
  • ROLLBACKは変更を取り消す(この場合はまだ何も変更してないけど、こういう書き方がいい感じ)。
重要!

このレクチャーはトランザクションの実際の使い方シナリオにフォーカスしてるから、リアルな例を出してみたよ。これはストアドプロシージャを含んでて、PL-SQLで書かれてる。もうみんなこれがどう動くか分かるくらいには経験積んでると思う。今後またPL-SQLの話に戻って、もっと難しい例もやるからね。

トランザクションでデータを一括更新

トランザクションは送金だけじゃなくて、他の用途でも便利だよ。例えば、ネットショップのデータベースで、毎日たくさんの注文が「配送中」から「完了」にステータス変更されるとする。もし一気にたくさんのレコードを更新して、途中で失敗した時に元に戻せるようにしたいなら?もちろんトランザクションを使うべき!

もう一つのシナリオ:注文ステータスの更新。

こんな感じ:

-- トランザクション開始
BEGIN;

-- ステップ1. 配送日が過去の注文を更新
UPDATE orders
SET status = 'completed'
WHERE delivery_date < CURRENT_DATE;

-- ステップ2. 更新成功を通知
RAISE NOTICE '全ての注文ステータスが正常に更新されました。';

-- 変更を確定
COMMIT;

もし何か失敗したら?

エラーの可能性はいつもある。例えば、WHERE条件をうっかり書き忘れて、全注文のステータスがcompletedになっちゃったとか。そういう時は、トランザクションを終わらせるか、明示的にロールバックするのが大事。

ロールバックのシナリオを見てみよう:

-- トランザクション開始
BEGIN;

-- ステップ1. 条件なしで注文を更新しようとした(やばい、ミス!)
UPDATE orders
SET status = 'completed';

-- エラーでトランザクションをロールバック
ROLLBACK;

-- これで注文は元のまま

SAVEPOINTでちょっと「柔軟さ」を追加

全部のトランザクションをロールバックする必要がない時もある。もしシナリオが複数のアクションで構成されてて、一部だけ取り消したい場合、SAVEPOINTが役立つよ。

今度のシナリオは:いくつかのステップを処理して、そのうちの一つだけロールバックできるようにする。

例えば、注文処理が複数のステップからなってて:在庫から商品を引く、注文ステータスを更新、顧客に通知を送る。もし通知がうまく送れなかったら、そのステップだけロールバックして、他の変更は残したい。

-- トランザクション開始
BEGIN;

-- ステップ1. 在庫から商品を引く
UPDATE products
SET stock = stock - 1
WHERE product_id = 101;

-- ロールバックポイントを保存
SAVEPOINT step1;

-- ステップ2. 注文ステータスを更新
UPDATE orders
SET status = 'shipped'
WHERE order_id = 202;

-- 顧客への通知を試みる
SAVEPOINT step2;
-- あっ、通知でエラー発生!
ROLLBACK TO SAVEPOINT step2;

-- これでトランザクションを安全に終わらせる
COMMIT;

まとめ

トランザクションはただの技術的なツールじゃなくて、データの一貫性を守る保証だよ。「ドミノ倒し効果」から守ってくれる。複数の関連する操作をやる時は、毎回「もしどれかが失敗したら?」って自分に聞いてみて。答えが「大惨事」なら、トランザクションを使うタイミングだよ。 覚えておいて:トランザクションを書くのに数分かける方が、障害後にデータを復旧するのに何時間もかけるよりずっといい。ユーザーも(君のメンタルも)きっと感謝してくれるよ!

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION