CodeGym /コース /SQL SELF /SAVEPOINTを使った部分的なロールバック管理

SAVEPOINTを使った部分的なロールバック管理

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

本を書いてると想像してみて。だけど、いつも計画通りにいくわけじゃないよね。たとえば、1章まるごと書いたけど、読み返すと5番目のパラグラフがひどい出来だったりする。どうする?全部ゴミ箱に捨てたりしないでしょ?気になるところだけ直すよね。

だいたいSAVEPOINTもPostgreSQLでそんな感じで使えるんだ。つまり:

  1. トランザクションの中でセーブポイントを作れる ― 本のしおりみたいなもん。
  2. そのポイントに戻って、そこから先の操作だけキャンセルできる。全部ロールバックしなくていい。
  3. 残りのデータはそのまま続けて作業できるし、最初からやり直す必要もない。

SAVEPOINTの基本構文

SAVEPOINT関連のコマンドはけっこうシンプル。基本セットはこんな感じ:

セーブポイントの作成(SAVEPOINT):

SAVEPOINT savepoint_name;

これは「この場所を覚えておこう、あとで戻るかもしれないし」って感じ。

作ったセーブポイントへのロールバック(ROLLBACK TO SAVEPOINT):
ROLLBACK TO SAVEPOINT savepoint_name;

何かうまくいかなかったら、そのSAVEPOINTに戻って、そこからの変更だけキャンセルできる。

セーブポイントの削除(RELEASE SAVEPOINT):
RELEASE SAVEPOINT savepoint_name;

これは「しおり」を外す感じ。これをやると、そのポイントにはもう戻れなくなる。

シンプルな例:ネットショップでの購入

ネットショップを運営してるとしよう。お客さんがカートにいくつか商品を入れて、注文処理と在庫テーブルの更新を含むトランザクションをやりたい。でも、どこかのステップで失敗したら、全部じゃなくて一部だけロールバックしたいよね。

BEGIN;

-- ステップ1: 商品「SQLの本」を予約
UPDATE inventory SET stock = stock - 1 WHERE product_id = 101;

-- セーブポイントを作成
SAVEPOINT book_reserved;

-- ステップ2: 商品「PostgreSQLマグカップ」を予約
UPDATE inventory SET stock = stock - 1 WHERE product_id = 102;

-- あれれ、マグカップが在庫切れだった!
ROLLBACK TO SAVEPOINT book_reserved;

-- 本だけの変更を確定
COMMIT;

この例で何が起きてるかというと:

  1. BEGINでトランザクションを開始。
  2. 本を予約したあと、book_reservedというセーブポイントを作成。これが最初の「チェックポイント」。
  3. マグカップも予約しようとしたけど、エラー発生(たとえば在庫がなかった)。
  4. book_reservedのセーブポイントにロールバックして、マグカップ関連の変更だけキャンセル。
  5. 最後にCOMMITで本の変更だけ確定。

もうちょい複雑な例:多段階データ処理

今度は注文管理システムで、orders(注文)、inventory(在庫)、billing(請求)の複数テーブルを更新するケースを考えてみよう。どこかで失敗しても、他のテーブルの進捗は失いたくない。ここでSAVEPOINTが役立つ。

BEGIN;

-- ステップ1: 新しい注文を作成
INSERT INTO orders (order_id, customer_id, status) VALUES (1, 123, 'pending');
SAVEPOINT after_order_created;

-- ステップ2: 在庫を更新
UPDATE inventory SET stock = stock - 2 WHERE product_id = 101;
SAVEPOINT after_stock_updated;

-- ステップ3: 支払い処理
INSERT INTO billing (order_id, amount, status) VALUES (1, 100, 'paid');

-- あっ、エラー:クレジットカードが拒否された!
ROLLBACK TO SAVEPOINT after_stock_updated;

-- 在庫更新後のステップに戻ったけど、注文は「pending」のまま。
UPDATE orders SET status = 'failed' WHERE order_id = 1;

COMMIT;

こんなふうにSAVEPOINTでトランザクションを論理的なステップに分けて、必要なポイントに戻りつつ、一部の変更は残せるんだ。

SAVEPOINTを使うときのコツ

  • セーブポイント名は意味のあるものにしよう。上の例だとafter_order_createdの方がstep1よりずっと分かりやすい。
  • ネストしたセーブポイントもOK:他のポイントにロールバックした中でもSAVEPOINTを作れる。
  • 不要なセーブポイントはRELEASE SAVEPOINTで解放してリソース節約。大きなトランザクションだとパフォーマンスも良くなるよ。

リアルな利用シーン

銀行取引の処理: たとえば複数口座間で送金する時、どれかが失敗したら特定のステップまでロールバックできる。

ファイルからのデータインポート: 大きなCSVファイルをインポートする時、各行ごとにチェックして、エラー行だけロールバック、成功分はそのまま残せる。

大量レコードの一括更新: 何千行も更新する複雑なSQLスクリプトでも、途中でエラーが出たら前のステップまで戻れる。

よくあるミスや落とし穴

たまにSAVEPOINTを使うと予想外のことが起きることもある。たとえば:

  • セーブポイントに戻るのを忘れたり、削除し忘れると、トランザクション終了までリソースがロックされることがある。
  • SAVEPOINTは作成前の操作はキャンセルできない。たとえばCOMMITで確定したデータはもうロールバックできない。

これでトランザクションの中で好きなところにセーブポイントを作って、安全に実験できるようになったね。これからもっとSQLの実践が待ってるから、次の冒険に備えておこう!

2
タスク
SQL SELF, レベル 39, レッスン 2
ロック未解除
`SAVEPOINT`を使ったシンプルな変更の取り消し
`SAVEPOINT`を使ったシンプルな変更の取り消し
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION