導入
前レッスンで、トランザクションは「まとめて成功、まとめて取り消し」だと学びました。ではその「取り消し」は、どんなときに起きるのでしょう。制約違反やエラーで処理が異常終了することを アボート(abort=中断) と呼びます。そして、「全部取り消し」と「全部確定」の“間”をとる仕組みが セーブポイント です。
説明
トランザクションの途中でエラー(UNIQUE 違反、NOT NULL 違反など)が起きると、その操作はアボートされ、多くの場面ではトランザクション全体を ROLLBACK して「なかったこと」にします。手動の ROLLBACK も、いわば「自分でアボートする」操作です。
sequenceDiagram participant U as あなた participant DB as データベース U->>DB: BEGIN U->>DB: INSERT(1件目)… 成功 U->>DB: INSERT(2件目・UNIQUE違反) DB-->>U: エラー! → アボート Note over U,DB: 中途半端な状態を残さず引き返せる(ROLLBACK)
エラーのたびに全部を巻き戻すのが惜しいこともあります。長い処理の途中に「ここまでは残したい」という区切りを置けるのが セーブポイント(SAVEPOINT) です。ゲームの「途中セーブ」に似ていて、失敗したら直前のセーブポイントまで戻せます。
BEGIN;
INSERT INTO users (id, name, age, city) VALUES (7, '渡辺', 25, '名古屋');
SAVEPOINT sp1; -- ここにしおりを挟む
INSERT INTO users (id, name, age, city) VALUES (8, '山本', 33, '京都');
ROLLBACK TO sp1; -- sp1 まで戻す(山本だけ取り消し)
COMMIT; -- 渡辺は残して確定
SELECT * FROM users;
SAVEPOINT 名前;… トランザクションの途中に「しおり」を置く。ROLLBACK TO 名前;… そのしおりの時点まで戻す(それ以降だけ取り消し。トランザクションは続く)。RELEASE 名前;… しおりを外す(そこまでの変更は確定側に残す)。
上の例では、7番の渡辺は残り、8番の山本だけが取り消されます。ROLLBACK(全部取り消し)と COMMIT(全部確定)の“間”をとれるのが、セーブポイントの役割です。「1000件のうち一部だけ失敗したら、その一部だけやり直す」といったバッチ処理で活躍します。
やってみよう
初期表示のトランザクション(渡辺を入れ、しおりを置き、山本を入れ、しおりまで戻して COMMIT)を実行し、そのあと SELECT * FROM users; を実行してください。渡辺(7番)は残り、山本(8番)は消えていることを確認しましょう。次に ROLLBACK TO sp1; の行を消してから同じものを実行すると、今度は山本も残ります。しおりの働きが体感できます。
演習
BEGIN で始め、伊藤(id=5)の city を ‘札幌’ に更新したあと SAVEPOINT sp1; を置き、さらに city を ‘那覇’ に更新してから、ROLLBACK TO sp1; で「那覇」への変更だけを取り消して COMMIT してください(結果として city は ‘札幌’ になります)。
ヒント1を見る
BEGIN; → UPDATE ... '札幌' WHERE id=5; → SAVEPOINT sp1; → UPDATE ... '那覇' WHERE id=5; → ROLLBACK TO sp1; → COMMIT; の順。
ヒント2を見る
BEGIN; UPDATE users SET city='札幌' WHERE id=5; SAVEPOINT sp1; UPDATE users SET city='那覇' WHERE id=5; ROLLBACK TO sp1; COMMIT;