本文へスキップ
BecomeCoder

SQL データ操作 (DML)コース · 第5章 安全に変更する · レッスン22

セーブポイントとアボート ― 途中で安全に引き返す

ブラウザで完結

導入

前レッスンで、トランザクションは「まとめて成功、まとめて取り消し」だと学びました。ではその「取り消し」は、どんなときに起きるのでしょう。制約違反やエラーで処理が異常終了することを アボート(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;

実際に動かしてみよう

下のエディタにクエリを書いて「実行」を押すと、ブラウザ内のSQLiteで結果が表示されます。本文の例をそのまま試したり、書き換えたりしてみましょう。

SQL — ブラウザ内で実行(SQLite)

ブラウザ内でSQLを動かす環境(SQLite)を読み込みます(初回のみ一瞬)。
スクロールして表示された時点でも自動で読み込まれます。