本を書いてると想像してみて。だけど、いつも計画通りにいくわけじゃないよね。たとえば、1章まるごと書いたけど、読み返すと5番目のパラグラフがひどい出来だったりする。どうする?全部ゴミ箱に捨てたりしないでしょ?気になるところだけ直すよね。
だいたいSAVEPOINTもPostgreSQLでそんな感じで使えるんだ。つまり:
- トランザクションの中でセーブポイントを作れる ― 本のしおりみたいなもん。
- そのポイントに戻って、そこから先の操作だけキャンセルできる。全部ロールバックしなくていい。
- 残りのデータはそのまま続けて作業できるし、最初からやり直す必要もない。
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;
この例で何が起きてるかというと:
BEGINでトランザクションを開始。- 本を予約したあと、
book_reservedというセーブポイントを作成。これが最初の「チェックポイント」。 - マグカップも予約しようとしたけど、エラー発生(たとえば在庫がなかった)。
book_reservedのセーブポイントにロールバックして、マグカップ関連の変更だけキャンセル。- 最後に
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の実践が待ってるから、次の冒険に備えておこう!
GO TO FULL VERSION