IMPLICIT TRANSACTIONSでコードをスッキリ!SQL Serverの単一ステートメントトランザクションの便利な機能
SQL Server における単一ステートメントトランザクションの詳細解説
わかりやすく例えると、銀行での振込処理を想像してみてください。
- 口座Aから1000円を引き出す
- 口座Bに1000円を預ける
この2つの操作は、単一ステートメントトランザクション で処理されます。
もし、この処理中にエラーが発生した場合、以下のようになります。
- 耐久性:
- 分離性:
- 一貫性:
- 原子性:
- どちらも行われない。
単一ステートメントトランザクション は、以下の3つの方法で実行できます。
- BEGIN TRANSACTION / COMMIT TRANSACTION を使用する
- SAVEPOINT を使用する
- IMPLICIT TRANSACTIONS 機能を使用する
BEGIN TRANSACTION;
-- 処理1を実行
UPDATE accounts
SET balance = balance - 1000
WHERE account_id = 'A';
-- 処理2を実行
UPDATE accounts
SET balance = balance + 1000
WHERE account_id = 'B';
COMMIT TRANSACTION;
SAVEPOINT before_transfer;
UPDATE accounts
SET balance = balance - 1000
WHERE account_id = 'A';
-- エラーが発生した場合、SAVEPOINTを使ってロールバック
IF @@ERROR <> 0
ROLLBACK TO before_transfer;
UPDATE accounts
SET balance = balance + 1000
WHERE account_id = 'B';
COMMIT TRANSACTION;
SET IMPLICIT_TRANSACTIONS ON;
UPDATE accounts
SET balance = balance - 1000
WHERE account_id = 'A';
UPDATE accounts
SET balance = balance + 1000
WHERE account_id = 'B';
- 短い処理を高速に実行したい場合
- エラー発生時に処理を自動的にロールバックしたい場合
- データの整合性を厳密に保ちたい場合
ただし、以下の点に注意する必要があります。
- ロックが発生しやすいため、パフォーマンスに影響を与える可能性がある
- 複雑な処理には向かない
-- 口座Aの残高を取得
DECLARE @accountA_balance INT;
SELECT @accountA_balance = balance
FROM accounts
WHERE account_id = 'A';
-- 口座Bの残高を取得
DECLARE @accountB_balance INT;
SELECT @accountB_balance = balance
FROM accounts
WHERE account_id = 'B';
-- BEGIN TRANSACTION でトランザクションを開始
BEGIN TRANSACTION;
-- 口座Aから1000円を引き出す
UPDATE accounts
SET balance = @accountA_balance - 1000
WHERE account_id = 'A';
-- エラーが発生した場合、トランザクションをロールバック
IF @@ERROR <> 0
ROLLBACK TRANSACTION;
-- 口座Bに1000円を預ける
UPDATE accounts
SET balance = @accountB_balance + 1000
WHERE account_id = 'B';
-- COMMIT TRANSACTION でトランザクションをコミット
COMMIT TRANSACTION;
-- 残高を確認
SELECT balance
FROM accounts
WHERE account_id = 'A';
SELECT balance
FROM accounts
WHERE account_id = 'B';
このコードの説明
- 最初に、
DECLARE
ステートメントを使って、@accountA_balance
と@accountB_balance
という変数を宣言します。 - 次に、
SELECT
ステートメントを使って、口座Aと口座Bの残高を取得します。 BEGIN TRANSACTION
ステートメントを使って、トランザクションを開始します。UPDATE
ステートメントを使って、口座Aから1000円を引き出します。IF @@ERROR <> 0
ステートメントを使って、エラーが発生したかどうかをチェックします。- エラーが発生した場合、
ROLLBACK TRANSACTION
ステートメントを使って、トランザクションをロールバックします。 - エラーが発生しなかった場合、
UPDATE
ステートメントを使って、口座Bに1000円を預けます。
このコードはあくまでも一例です。 実際の状況に合わせて、適宜修正してください。
- このコードは、短くてシンプルな処理を対象としています。複雑な処理の場合は、複数のステートメントトランザクションを使用する必要があります。
- このコードでは、ロックを考慮していません。ロックが必要な場合は、適切なロックメカニズムを使用する必要があります。
-- SAVEPOINT before_transfer を作成
SAVEPOINT before_transfer;
-- 口座Aから1000円を引き出す
UPDATE accounts
SET balance = balance - 1000
WHERE account_id = 'A';
-- エラーが発生した場合、SAVEPOINTを使ってロールバック
IF @@ERROR <> 0
ROLLBACK TO before_transfer;
-- 口座Bに1000円を預ける
UPDATE accounts
SET balance = balance + 1000
WHERE account_id = 'B';
-- エラーが発生しなかった場合、COMMIT
COMMIT TRANSACTION;
- 最初に、
SAVEPOINT before_transfer
という名前の SAVEPOINT を作成します。 - エラーが発生した場合、
ROLLBACK TO before_transfer
ステートメントを使って、SAVEPOINT までロールバックします。
- 複雑な処理に柔軟に対応 できます。複数の処理を組み合わせた複雑な処理でも、適切な SAVEPOINT を配置することで、必要な部分だけをロールバックすることができます。
- 部分的なロールバックが可能 です。エラーが発生した場合、処理の一部だけをロールバックし、残りの処理は続行することができます。
- SAVEPOINT はトランザクション内でのみ有効 です。トランザクションがコミットまたはロールバックされると、SAVEPOINT は破棄されます。
- SAVEPOINT はネストできません。入れ子状に SAVEPOINT を作成することはできません。
IMPLICIT TRANSACTIONS 機能は、BEGIN TRANSACTION / COMMIT TRANSACTION を明示的に記述しなくても、単一ステートメントトランザクションを実行できる機能です。
IMPLICIT TRANSACTIONS 機能を使用するには、以下の設定が必要です。
SET IMPLICIT_TRANSACTIONS ON;
-- 口座Aから1000円を引き出す
UPDATE accounts
SET balance = balance - 1000
WHERE account_id = 'A';
-- エラーが発生した場合、ロールバック
IF @@ERROR <> 0
ROLLBACK TRANSACTION;
-- 口座Bに1000円を預ける
UPDATE accounts
SET balance = balance + 1000
WHERE account_id = 'B';
- コードが簡潔になる です。BEGIN TRANSACTION / COMMIT TRANSACTION を記述する必要がないため、コードが読みやすくなり、メンテナンス性も向上します。
- SAVEPOINT を使用できない です。IMPLICIT TRANSACTIONS 機能では、SAVEPOINT を使用できません。
- 意図しないトランザクションが発生する可能性 があります。IMPLICIT TRANSACTIONS 機能を有効にすると、すべての DML ステートメントが単一ステートメントトランザクションとして実行されるため、意図しないトランザクションが発生する可能性があります。
sql sql-server transactions