SQL ServerのROLLBACKは、まだ確定していないトランザクション内の変更を取り消す命令です。UPDATEを実行した後ならいつでも戻せる、という機能ではありません。

最初に確認すること
未確定のトランザクションがある → ROLLBACKで取り消せます。
COMMIT済み・自動コミット済み → ROLLBACKでは戻せません。
操作は、更新に使った同じSSMSの接続で行います。

ここではWindowsのSQL Server LocalDBとSSMSを使い、練習用の商品データで「100000 → 120000 → 100000」を確認します。SQL Server用の構文なので、MySQLやOracleの手順とは分けてください。

ROLLBACKとCOMMITの違い

BEGIN TRANSACTION:変更をひとまとまりにする処理を開始します。

ROLLBACK TRANSACTION:そのトランザクションの変更を取り消して終了します。

COMMIT TRANSACTION:変更を確定して終了します。

この練習の流れ
開始前:価格100000
↓ BEGIN TRANSACTION → UPDATE
未確定:価格120000
↓ ROLLBACK TRANSACTION
終了後:価格100000

名前を付けずにROLLBACK TRANSACTIONを実行すると、直前の1文だけでなく、現在のトランザクション内の変更をまとめて取り消します。INSERTやDELETEも同じ考え方です。TRANSACTIONはTRANと省略できます。

SQL Serverの単独トランザクションで価格100000を120000に更新し、ROLLBACKなら100000へ戻り、COMMITなら120000で確定する概念図。確定後はROLLBACKできない。
ROLLBACKとCOMMITの分岐を示す説明用の概念図です。SSMSの実画面ではありません。同じ接続で、BEGINを重ねない単独のトランザクションを前提にしています。出典:Microsoft Learn「Transactions」「ROLLBACK TRANSACTION」「COMMIT TRANSACTION」を基に作成。

準備:練習用DBと商品データを作る

SSMSで(LocalDB)\MSSQLLocalDBへ接続し、練習専用の新しいクエリを開きます。接続準備やC#.NETからの利用はSQL Server LocalDBの導入とSSMS接続を参考にしてください。

最初に接続先とトランザクションの状態を確認します。TransactionCountとTransactionStateが両方0であることを確認してから進めます。0以外なら、その接続で何の作業が残っているかを確認し、理由がわからないままCOMMITやROLLBACKを実行しないでください。

SELECT
    DB_NAME() AS CurrentDatabase,
    @@SPID AS SessionId,
    @@TRANCOUNT AS TransactionCount,
    XACT_STATE() AS TransactionState;

1. RollBackDBを新規作成する

次のSQLは最初に1回だけ実行します。同名DBが既にあるというエラーが出たら先へ進まず、未使用の名前を選び、以降のUSEの名前もそろえてください。既存データを削除して練習をやり直す必要はありません。

CREATE DATABASE RollBackDB;

2. テーブルと初期データを用意する

作成が成功したら、同じクエリで次を1回だけ実行します。ここではこの接続のIMPLICIT_TRANSACTIONSをOFFにし、BEGIN TRANSACTIONで開始する方法にそろえます。

USE RollBackDB;
SET IMPLICIT_TRANSACTIONS OFF;

CREATE TABLE dbo.Products
(
    ProductId INT NOT NULL PRIMARY KEY,
    ProductName NVARCHAR(100) NOT NULL,
    Price INT NOT NULL
);

INSERT INTO dbo.Products
    (ProductId, ProductName, Price)
VALUES
    (1, N'ノートパソコン', 100000),
    (2, N'マウス', 3000),
    (3, N'キーボード', 5000);

SELECT ProductId, ProductName, Price
FROM dbo.Products
ORDER BY ProductId;

ProductIdは商品を識別する主キー、ProductNameは商品名、Priceは価格です。N付きの文字列はUnicodeとして扱います。結果が次の3件になれば準備完了です。

商品ID商品名価格
1ノートパソコン100000
2マウス3000
3キーボード5000

UPDATEしてからROLLBACKする

次のコードは全体を選択して1回実行します。SSMSでは一部だけ選択すると選択範囲だけが実行されるため、BEGINからROLLBACKまでを漏らさないようにしてください。

USE RollBackDB;

-- この練習は未完了の作業がない状態で開始
IF @@TRANCOUNT <> 0
    THROW 50001, N'未完了のトランザクションがあります。', 1;

SELECT N'更新前' AS Stage, ProductId, Price
FROM dbo.Products WHERE ProductId = 1;

BEGIN TRANSACTION;

UPDATE dbo.Products
SET Price = 120000
WHERE ProductId = 1;

SELECT @@ROWCOUNT AS UpdatedRowCount;

SELECT N'更新後' AS Stage, ProductId, Price
FROM dbo.Products WHERE ProductId = 1;

ROLLBACK TRANSACTION;

SELECT N'取消後' AS Stage, ProductId, Price
FROM dbo.Products WHERE ProductId = 1;

SELECT @@TRANCOUNT AS TransactionCount;

UpdatedRowCountは1、最後のTransactionCountは0になります。価格の変化は次のとおりです。@@ROWCOUNTは直前の文の件数なので、UPDATEのすぐ後で読みます。

確認時点価格状態
更新前100000変更前
更新後120000同じ接続内で見える未確定の値
取消後100000更新を取り消した値

途中の結果を見ながら試す場合は、更新後のSELECTまで実行し、確認後すぐにROLLBACK以降を実行します。未確定のまま離席しないでください。エラーが出たときは次の例へ進む前に、下の「状態の確認」を行います。

WHERE句と対象件数の確認は、ROLLBACKとは別に必要です。条件の書き方はUPDATEの使い方、削除の練習はDELETEの使い方で確認できます。

複数の更新もまとめて取り消せる

同じトランザクション内にUPDATEが2つあれば、ROLLBACKで両方取り消せます。次の例は準備したデータを使い、TransactionCountが0の状態で全体を実行します。

USE RollBackDB;

IF @@TRANCOUNT <> 0
    THROW 50001, N'未完了のトランザクションがあります。', 1;

BEGIN TRANSACTION;

UPDATE dbo.Products SET Price = 3500
WHERE ProductId = 2;
UPDATE dbo.Products SET Price = 6000
WHERE ProductId = 3;

SELECT ProductId, Price
FROM dbo.Products WHERE ProductId IN (2, 3);

ROLLBACK TRANSACTION;

SELECT ProductId, Price
FROM dbo.Products WHERE ProductId IN (2, 3);

マウスは3000 → 3500 → 3000、キーボードは5000 → 6000 → 5000と変化します。ROLLBACKの前に実行した2つのUPDATEが、どちらも取り消されます。

確定する場合はCOMMITを使う

次は練習データの価格を120000に確定する例です。取り消す例と区別して実行してください。

USE RollBackDB;

IF @@TRANCOUNT <> 0
    THROW 50001, N'未完了のトランザクションがあります。', 1;

BEGIN TRANSACTION;

UPDATE dbo.Products
SET Price = 120000
WHERE ProductId = 1;

COMMIT TRANSACTION;

SELECT ProductId, Price
FROM dbo.Products WHERE ProductId = 1;
SELECT @@TRANCOUNT AS TransactionCount;

この例では価格120000が確定し、TransactionCountは0になります。後からROLLBACKしても100000には戻りません。実務で誤って確定した場合は、更新を重ねる前に管理者と復旧方法を確認してください。バックアップや更新前の記録の有無によって対応が変わります。

確定後に同じ練習を続けると、更新前の値も120000になります。最初の例と同じ結果を確認したいときは、新しい名前の練習用DBで準備から始めてください。確定処理やエラー時のTRY…CATCHはCOMMITの使い方も参考になります。

ROLLBACKできないときは、接続と状態を確認する

SELECT
    DB_NAME() AS CurrentDatabase,
    @@SPID AS SessionId,
    @@TRANCOUNT AS TransactionCount,
    XACT_STATE() AS TransactionState;

「対応するBEGIN TRANSACTIONがありません」
その接続に取り消すトランザクションがありません。すでにCOMMIT/ROLLBACK済みか、自動コミットで確定したか、別の接続を見ていないか確認します。後からBEGINを追加しても、確定済みの更新は戻せません。

別タブでROLLBACKしたのに戻らない
別セッションのトランザクションは操作できません。更新に使ったクエリへ戻り、SessionIdを確認します。GOはSSMSのバッチ区切りで、COMMITの代わりではありません。

TransactionCountが1以上のまま
この接続に未完了のトランザクションがあります。何を更新したか確認し、確定するか取り消すかを判断します。保持しているロックによって他の処理が待たされることがあります。

TransactionState意味
0アクティブなユーザートランザクションなし
1トランザクションあり。確定可能
-1エラーにより確定不可。全体のROLLBACKが必要

@@TRANCOUNTだけでは確定可能か判断できません。練習専用の接続で、残っている変更をすべて取り消すと決めた場合はROLLBACK TRANSACTIONを実行し、再度状態を確認します。

Productsが見つからない・主キーの重複エラー
CurrentDatabaseがRollBackDBか、準備が正常に終わったか確認します。CREATEやINSERTの繰り返しによるエラーなら、既存データを消すのではなくSELECTで内容を確認してください。

自動コミットと暗黙のトランザクション
IMPLICIT_TRANSACTIONSがOFFで明示的なトランザクションを開始していない場合、UPDATEなどは文単位で自動コミットされます。ONの場合はUPDATEなどで暗黙に開始されるため、BEGINを書いていなくても未確定のトランザクションが残ることがあります。

補足:一部だけ戻すならSAVE TRANSACTION

名前なしのROLLBACKは全体を取り消します。一部だけ戻すには、事前にセーブポイントを置きます。次の例はマウスの更新を残してキーボードの更新だけ取り消し、最後に練習全体を取り消します。

USE RollBackDB;

IF @@TRANCOUNT <> 0
    THROW 50001, N'未完了のトランザクションがあります。', 1;

BEGIN TRANSACTION;

UPDATE dbo.Products SET Price = 3500
WHERE ProductId = 2;

SAVE TRANSACTION BeforeKeyboard;

UPDATE dbo.Products SET Price = 6000
WHERE ProductId = 3;

ROLLBACK TRANSACTION BeforeKeyboard;

SELECT ProductId, Price
FROM dbo.Products WHERE ProductId IN (2, 3);

-- セーブポイントへ戻しても全体は終了しない
ROLLBACK TRANSACTION;

SELECTではマウス3500、キーボード5000になります。セーブポイントへのROLLBACKでは@@TRANCOUNTは減りません。XACT_STATEが-1のときや分散トランザクションでは、この方法で一部だけ戻すことはできません。

BEGINを重ねた場合も、内側のCOMMITだけで変更が確定するわけではありません。名前なしのROLLBACKは外側まで取り消します。この記事の基本手順は、BEGINを重ねない単独のトランザクションを前提にしています。

まとめ

まずは練習用DBで、開始 → 更新 → 確認 → 取消 → 再確認を1つの接続で試してみましょう。ROLLBACKできるのは未確定の変更です。対象のWHERE条件と件数を確認し、最後にトランザクションが残っていないことまで確かめると、手順を取り違えにくくなります。

参考:Microsoft:ROLLBACK TRANSACTION / @@TRANCOUNT / XACT_STATE / IMPLICIT_TRANSACTIONS