「半分だけ更新されたデータ」という悪夢を防ぐ。Access VBAでトランザクションを使いこなす極意
こんにちは。業務自動化の世界へようこそ。
マクロの記録から一歩踏み出し、VBAという「魔法の杖」を手に取ったあなた。素晴らしい決断です。
しかし、コードが書けるようになった先に、エンジニアを震え上がらせる恐ろしい現象が待ち受けています。それは、「データが中途半端に書き換わってしまう」という現象です。
例えば、銀行の振込処理を想像してみてください。
「Aさんの口座から1万円引く」という処理は成功した。でも、その直後の「Bさんの口座に1万円足す」という処理で、ネットワークエラーや予期せぬエラーが起きて止まってしまったら……?
Aさんの1万円は、この世から消えてしまいますよね。これが、データベースにおける「不整合」という名の悪夢です。
今日は、この悪夢を未然に防ぐための最強の武器、「トランザクション(ロールバック)」の使い方を伝授します。ここをマスターすれば、あなたのプログラムの信頼性はプロの領域へと跳ね上がりますよ。
1. 「トランザクション」とは何か?
プログラミングの世界には、「All or Nothing(すべてか、無か)」という鉄則があります。
一連の更新処理(例えば、在庫を減らす、売上を作る、履歴を残す、といった複数のステップ)を、「一つのまとまり」として扱う考え方をトランザクションと呼びます。
トランザクションには、3つの重要な状態があります。
1. BeginTrans(開始): 「今から一連の作業を始めるよ。まだ確定(保存)はしないよ」という合図。
2. CommitTrans(確定): 「すべての作業が完璧に終わった! これでデータベースに永久に保存してくれ!」という命令。
3. Rollback(取り消し): 「ああっ、エラーだ! 今までの作業は全部なかったことにして、作業開始前の状態に戻してくれ!」という命令。
この「Rollback」こそが、データ不整合を防ぐ守護神なのです。
2. DAOにおけるトランザクションの構造
Access VBAでデータを操作する際、私たちはよく `DAO.Recordset` を使います。
しかし、トランザクションの命令を出す相手は、Recordset(レコードセット)ではなく、Workspace(ワークスペース)という、データベース全体を管理する「司令塔」なのです。
図解すると、このような階層構造になっています。
[DBEngine] (データベースの心臓部)
└── [Workspace] (作業領域:ここでトランザクションを管理!)
└── [Database] (実際のファイル)
└── [Recordset] (個々のデータ)
トランザクションを制御するには、この「Workspace」を正しく操作する必要があります。
3. 【実践】エラー発生時に確実にロールバックする定型句
それでは、実戦でそのまま使えるテンプレートコードを見ていきましょう。
今回は「複数のテーブルを更新する」というシナリオを想定した、最も堅牢な書き方です。
Option Compare Database
Option Explicit
''' <summary>
''' データの更新処理を行うメインルーチン
''' </summary>
Public Sub ExecuteSafeUpdate()
' --- 1. 変数の宣言 ---
Dim ws As DAO.Workspace
Dim db As DAO.Database
Dim rsStock As DAO.Recordset
Dim rsSales As DAO.Recordset
' エラーハンドリングの設定
' エラーが発生したら "Err_Handler:" というラベルへジャンプするように指示
On Error GoTo Err_Handler
' --- 2. オブジェクトのセットアップ ---
' トランザクションを管理するのは Workspace です
Set ws = DBEngine(0)
Set db = CurrentDb
' --- 3. トランザクションの開始 ---
' ここから先の一連の処理は「仮の状態」になります
ws.BeginTrans
Debug.Print "トランザクションを開始しました。"
' --- 4. メインの処理(ここが重要!) ---
' 例1:在庫テーブルの更新
Set rsStock = db.OpenRecordset("T_Inventory", dbOpenDynaset)
rsStock.FindFirst "ProductID = 1"
If Not rsStock.EOF Then
rsStock.Edit
rsStock.Fields("StockCount").Value = rsStock.Fields("StockCount").Value - 1
rsStock.Update ' ここで更新されるが、Commitされるまでは確定しない
End If
rsStock.Close
' --- (ここでエラーが起きると仮定しましょう) ---
' 例:あえてエラーを発生させるテスト用コード
' Err.Raise vbObjectError + 1, "Test", "強制的なエラー発生!"
' 例2:売上履歴テーブルへの追加
Set rsSales = db.OpenRecordset("T_SalesHistory", dbOpenDynaset)
rsSales.AddNew
rsSales.Fields("ProductID").Value = 1
rsSales.Fields("SaleDate").Value = Now()
rsSales.Update
rsSales.Close
' --- 5. すべて成功した場合のみ、確定(コミット)する ---
ws.CommitTrans
Debug.Print "すべての処理が成功しました。データを確定しました。"
MsgBox "更新が完了しました。", vbInformation
Exit_Sub:
' --- 6. 後片付け(クリーンアップ) ---
' エラーが起きても起きなくても、開いたオブジェクトは必ず閉じる
On Error Resume Next ' 後片付け中のエラーで止まらないように
If Not rsStock Is Nothing Then rsStock.Close
If Not rsSales Is Nothing Then rsSales.Close
Set rsStock = Nothing
Set rsSales = Nothing
Set db = Nothing
Set ws = Nothing
Exit Sub
Err_Handler:
' --- 7. エラー発生時の処理(ロールバック) ---
' エラーが起きたら、BeginTrans 以前の状態にすべて巻き戻す!
If Not ws Is Nothing Then
ws.Rollback
Debug.Print "エラーが発生したため、ロールバックを実行しました。"
End If
' エラー内容をユーザーに通知
MsgBox "エラーが発生したため、処理を中断し、データを元の状態に戻しました。" & vbCrLf & _
"エラー内容: " & Err.Description, vbCritical, "更新失敗"
Resume Exit_Sub
End Sub
4. ここがプロの分かれ道!陥りやすい落とし穴
このコードを読み解く上で、初心者が躓きやすいポイントを3つお伝えします。ここをクリアすれば、あなたはもう「ただのマクロ使い」ではありません。
① `Update` メソッドと `CommitTrans` の違い
「`rs.Update` を実行したんだから、もう保存されたんじゃないの?」と思うかもしれません。
いいえ、違います。`rs.Update` は「このレコードの変更内容を確定させる」という意味ですが、トランザクションの中では「仮の確定」に過ぎません。`ws.CommitTrans` が呼ばれるまでは、他のユーザーからも、データベース本体からも、その変更は見えない(あるいは変更中として扱われる)のです。
② エラーハンドリングの「場所」
`On Error GoTo Err_Handler` は、必ず `BeginTrans` よりも前に書いてください。
もし `BeginTrans` の直後にエラーが起きた場合、エラーハンドラーが準備できていないと、プログラムはそのまま強制終了してしまいます。そうなると、トランザクションが「開きっぱなし」になり、データベースがロックされて他の人が操作できなくなる……という最悪の事態を招きます。
③ 後片付け(Cleanup)の徹底
`Exit Sub` の直前に、必ずオブジェクトを `Nothing` にしたり `Close` したりする処理を入れてください。これを怠ると、メモリを無駄に消費したり、予期せぬタイミングでファイルがロックされたりします。プロのコードは、「入り口よりも出口(後片付け)に魂を込める」のです。
まとめ:信頼されるシステムを作るために
いかがでしたか?
「トランザクション」と「ロールバック」という概念は、最初は少し難しく感じるかもしれません。しかし、一度この仕組みを体に叩き込んでしまえば、あなたは「データが壊れる心配のない、信頼されるシステム」を作れるようになります。
「エラーが起きることを前提に、どうやって元に戻すか」
この考え方こそが、エンジニアリングの本質です。
ぜひ、今日学んだテンプレートを自分のプロジェクトに組み込んでみてください。一歩ずつ、確実に、最強のエンジニアへの道を歩んでいきましょう。
応援していますよ!
