この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:6
ページ更新者:guest
更新日時:2026-06-11 07:29:05

タイトル: SAVEPOINT
SEOタイトル: SQL SAVEPOINT の使い方 - 部分ロールバック・ネストトランザクション完全ガイド

この記事の要点
  • SAVEPOINT はトランザクション内に "しおり" を置き、そこまで部分ロールバック可能にする命令
  • SAVEPOINT name で設置 / ROLLBACK TO SAVEPOINT name で巻き戻し / RELEASE SAVEPOINT name で解放
  • SAVEPOINT は BEGIN ... COMMIT の間でのみ有効。COMMIT/ROLLBACK 時点で全 SAVEPOINT 消滅
  • ネスト可能。多くのフレームワーク(Spring、Laravel)はネストトランザクションを SAVEPOINT で実装
  • 同名 SAVEPOINT を上書き設置すると古い方は失われる。エラーハンドリングのリトライ起点としても便利

SAVEPOINT とは

SAVEPOINT は SQL 標準で定義されたトランザクション制御命令で、トランザクション内に名前付きの "チェックポイント" を作成します。エラー発生時に SAVEPOINT まで部分的にロールバックでき、その後の処理は継続可能です。

通常の ROLLBACK はトランザクション全体を巻き戻すのに対し、ROLLBACK TO SAVEPOINT指定地点までの巻き戻しに留まります。これにより複雑な業務処理の一部だけをやり直すことができます。

基本構文

-- 設置
SAVEPOINT sp_name;

-- 巻き戻し(SAVEPOINT 自体は残り、再利用可能)
ROLLBACK TO SAVEPOINT sp_name;
ROLLBACK TO sp_name;          -- SAVEPOINT 省略可(多くの DB)

-- 解放(明示的に SAVEPOINT を消す)
RELEASE SAVEPOINT sp_name;

典型的な使用例

3 段階の処理を行い、2 段階目がエラーでも 1 段階目は維持したいケース:

BEGIN;

-- 1段階目: 顧客登録
INSERT INTO customers (id, name) VALUES (1, 'Alice');
SAVEPOINT after_customer;

-- 2段階目: 任意の住所登録(エラー許容)
INSERT INTO addresses (customer_id, addr) VALUES (1, 'Tokyo');
-- もし制約違反やビジネスロジックで失敗したら
ROLLBACK TO after_customer;
-- → addresses への INSERT は消える、customers は残る

-- 3段階目: ポイント付与(必須)
INSERT INTO points (customer_id, point) VALUES (1, 100);

COMMIT;
-- → customers と points は確定、addresses は無し

ROLLBACK との違い

命令巻き戻し範囲トランザクションは継続するか
ROLLBACKBEGIN 以降すべて終了する
ROLLBACK TO SAVEPOINT spSAVEPOINT 以降のみ継続する(再 COMMIT 可能)
COMMIT巻き戻し無し(確定)終了する
RELEASE SAVEPOINT sp巻き戻し無し(SAVEPOINT 削除のみ)継続する

ネストして使う

BEGIN;

INSERT INTO orders VALUES (1, 'A');
SAVEPOINT sp1;

  INSERT INTO orders VALUES (2, 'B');
  SAVEPOINT sp2;

    INSERT INTO orders VALUES (3, 'C');
    SAVEPOINT sp3;

    INSERT INTO orders VALUES (4, 'D');
    ROLLBACK TO sp3;
    -- 4 のみ消える。1,2,3 は残る

  ROLLBACK TO sp2;
  -- 3 も消える。1,2 が残る(sp3 も自動消滅)

COMMIT;
-- 1, 2 が確定

注意: ROLLBACK TO sp2 を行うと、sp2 より後に作られた sp3 は自動的に消滅します。これは SQL 標準で規定された動作です。

DBMS 別の対応状況

DBMS対応特記事項
PostgreSQL完全対応SQL 標準準拠。ROLLBACK TO sp 形式 OK
MySQL / MariaDB対応(InnoDB のみ)MyISAM は不可。SAVEPOINT / ROLLBACK TO / RELEASE
Oracle完全対応SAVEPOINT / ROLLBACK TO SAVEPOINT。RELEASE は無し(自動管理)
SQL Server対応(独自構文)SAVE TRANSACTION sp / ROLLBACK TRANSACTION sp
SQLite対応SQL 標準準拠
DB2対応SAVEPOINT sp ON ROLLBACK RETAIN CURSORS

SQL Server の独自構文

-- SQL Server は SAVE TRANSACTION
BEGIN TRANSACTION;

INSERT INTO orders VALUES (1, 'A');
SAVE TRANSACTION sp1;

INSERT INTO orders VALUES (2, 'B');
ROLLBACK TRANSACTION sp1;    -- sp1 以降ロールバック

COMMIT TRANSACTION;

エラーハンドリングとリトライ

外部 API 呼び出しや楽観ロックなど、失敗の可能性がある処理は SAVEPOINT を挟むと安全です:

BEGIN;

INSERT INTO orders (id, total) VALUES (100, 5000);
SAVEPOINT before_payment;

BEGIN
  -- 外部決済 API 呼び出し(失敗するかもしれない)
  PERFORM call_payment_api(100);
EXCEPTION WHEN OTHERS THEN
  ROLLBACK TO before_payment;
  -- 失敗時は order を「未払い」状態で残す
  UPDATE orders SET status = 'PAYMENT_FAILED' WHERE id = 100;
END;

COMMIT;

フレームワークでの利用

Spring (Java) のネストトランザクション

@Service
public class OrderService {

    @Transactional
    public void placeOrder(Order o) {
        customerService.register(o.getCustomer());
        try {
            inventoryService.reserve(o);   // NESTED
        } catch (StockException e) {
            // 在庫不足 → reserve だけ巻き戻し、registerは生きる
            log.warn("Stock failure, continue without reservation");
        }
    }
}

@Service
public class InventoryService {
    // 親トランザクションに SAVEPOINT を作る
    @Transactional(propagation = Propagation.NESTED)
    public void reserve(Order o) { ... }
}

Spring は Propagation.NESTED 指定時に内部で Connection#setSavepoint() を呼び、SAVEPOINT ベースのネストを実現します。

Laravel (PHP) のネストトランザクション

DB::transaction(function () {
    DB::table('customers')->insert([...]);

    try {
        // ネスト = 内部で SAVEPOINT 発行
        DB::transaction(function () {
            DB::table('addresses')->insert([...]);
        });
    } catch (Throwable $e) {
        // addresses INSERT のみロールバック
        Log::warning('addr failed but continue');
    }

    DB::table('points')->insert([...]);
});

よくあるトラブル

症状原因対処
ERROR: SAVEPOINT can only be used in transaction blocksBEGIN していない事前に BEGIN;
savepoint "sp" does not exist既に ROLLBACK で消滅した SAVEPOINT を参照名前を見直し
自動コミットモードで動かないクライアントが auto-commit ONSET autocommit = 0;
MySQL で MyISAM テーブル非トランザクションエンジンInnoDB へ変換

FAQ

Q: SAVEPOINT を多用しても性能に影響しない?
A: 軽微な影響はあります。InnoDB / PostgreSQL ともに、各 SAVEPOINT で内部状態を記録するためメモリ消費が増えます。常識的な数(数十程度)なら問題ありません。

Q: 同じ名前の SAVEPOINT を再度 SAVEPOINT 文で作るとどうなる?
A: 多くの DB で古い SAVEPOINT が消滅し、新しい位置に作り直される動作になります(PostgreSQL は両方残る独自仕様)。挙動が DB によって違うので、名前は使い回さないのが安全です。

Q: COMMIT したら SAVEPOINT は?
A: トランザクション内のすべての SAVEPOINT はCOMMIT / ROLLBACK と同時に消滅します。トランザクションをまたぐ SAVEPOINT は存在しません。