こんにちは!日々のAccess開発、お疲れ様です。
マクロの記録から一歩踏み出し、「自分でコードを書いてシステムを動かしたい!」という熱意、素晴らしいですね。ここをクリアすれば、あなたも立派なVBAエンジニアの仲間入りです。
今日は、Access VBAで大量のデータを扱うときに避けて通れない「爆速・一括処理の極意」についてお話しします。
「VBAで何千件ものデータを更新したら、画面が固まった挙句にもの凄く時間がかかる…」
そんな絶望を味わったことはありませんか?
今回は、その原因を断ち切り、プロの現場で使われている「トランザクション管理による一括更新の高速化」の技術を、優しく、そして徹底的に解説していきますね。ここさえ押さえれば、あなたの作るAccessアプリは見違えるほど軽快になりますよ!
—
1. なぜ「1件ずつの更新」は遅いのか?
まずは、なぜ普通のループ処理が遅いのか、その裏側の仕組みを知りましょう。
例えば、1,000件の顧客データを「VIP会員」に書き換える処理を想像してください。
- 素朴なループ処理(NGパターン)
1件書き換えるたびに、Access(厳密にはデータベースエンジンのJet/ACE)は「よし、ディスクに書き込むぞ!」と毎回お辞儀をしてディスクに保存しにいっています。これが1,000回繰り返されます。そりゃあ遅いはずですよね。
- 今回目指す「トランザクション一括処理(神パターン)」
「1,000件分の変更メモを私の手元(メモリ)にまとめておくから、最後の『保存』の合図が出るまでディスクには書きに行かないで!」と指示します。そして、最後に「一括保存!」と叫ぶのです。ディスクとの往復が1回で済むため、処理スピードは劇的に向上(数十倍〜数百倍)します。
AccessのDAO(Data Access Objects)には、ADOの `BatchUpdate` のような一発のメソッドはありませんが、「トランザクション(BeginTrans / CommitTrans)」を明示的に使うことで、これと全く同じ高速化を完璧に再現できるのです。
—
2. 実践!トランザクションによる高速化コード
百聞は一見に如かず。実際のコードを見てみましょう。
今回は、`T_顧客` というテーブルの、未発送のステータスを一気に「処理済み」に更新するシナリオを想定しています。
開発画面の標準モジュールに、以下のコードを貼り付けてみてください。
Sub FastUpdateWithTransaction()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim startTime As Double
‘ 処理時間を計測するためのタイマー開始
startTime = Timer
‘ 現在のデータベースを取得
Set db = CurrentDb
‘ 更新対象のレコードセットを開く(ダイナセットまたはスナップショット)
‘ ※今回は書き込みを行うため dbOpenDynaset を使用
Set rs = db.OpenRecordset(“SELECT FROM T_顧客 WHERE ステータス = ‘未発送'”, dbOpenDynaset)
‘ 対象データがなければ終了
If rs.EOF Then
MsgBox “更新対象のデータはありません。”, vbInformation
GoTo CleanUp
End If
‘ ==========================================
‘ ★ここが最重要!トランザクションの開始
‘ ==========================================
db.BeginTrans
On Error GoTo ErrorHandler ‘ エラー時のロールバック(巻き戻し)に備える
Do While Not rs.EOF
‘ 編集モードに入る
rs.Edit
‘ データの書き換え
rs!ステータス = “処理済み”
rs!更新日時 = Now()
‘ 変更を確定(この時点ではまだメモリ上、あるいは一時領域のことが多い)
rs.Update
‘ 次のレコードへ
rs.MoveNext
Loop
‘ ==========================================
‘ ★すべての処理が成功したらコミット(一括確定)
‘ ==========================================
db.CommitTrans
MsgBox “処理が完了しました! 実行時間: ” & Format(Timer – startTime, “0.00秒”), vbInformation
GoTo CleanUp
ErrorHandler:
‘ 万が一エラーが発生した場合、変更をすべて白紙に戻す(ロールバック)
db.Rollback
MsgBox “エラーが発生したため、処理を中断し変更を破棄しました。” & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical
CleanUp:
‘ オブジェクトの解放(メモリリーク防止の鉄則!)
If Not rs Is Nothing Then
rs.Close
Set rs = Nothing
End If
Set db = Nothing
End Sub
—
3. コードのポイントと「初心者が陥りやすい罠」
先輩エンジニアとして、このコードの重要なポイントと、初心者がやりがちな失敗について解説しておきますね。
① `BeginTrans` と `CommitTrans` のペアを忘れない
トランザクションを開いたら(`BeginTrans`)、必ず終わりにはコミット(`CommitTrans`)しなければなりません。これがないと、データベースがロックされたままになり、他の操作ができなくなってしまいます。
② エラー処理(`Rollback`)の重要性
もし、1,000件中の500件目を更新した瞬間にエラー(ディスク容量不足や型の不一致など)が起きたらどうなるでしょう?
トランザクションを使っていれば、`Rollback` によって「処理前の綺麗な状態」に一瞬で巻き戻すことができます。中途半端な汚いデータがテーブルに残るのを防ぐ、これがプロの安全設計です。
③ オブジェクトの解放(CleanUp)
コードの最後にある `rs.Close` と `Set … = Nothing`。
「めんどくさいから書かなくても動くしいいか」と思ってはいけません。これをサボると、VBAのメモリ上にゴミが残り続け、Accessが突然フリーズする原因(メモリリーク)になります。後かたづけまでがプロの仕事ですよ。
—
4. さらに上を目指すあなたへ:SQLという最強の選択肢
ここまでDAOのレコードセットを使ったトランザクションを解説しましたが、実は「条件に合致するデータを一括で書き換える」だけであれば、VBAでループを回すよりも、SQLの `UPDATE` ステートメントを `CurrentDb.Execute` で実行する方が、さらに圧倒的に速く、コードもシンプルになります。
Sub UltraFastSQLUpdate()
Dim sql As String
‘ SQL文で一撃で書き換える
sql = “UPDATE T_顧客 SET ステータス = ‘処理済み’, 更新日時 = Now() WHERE ステータス = ‘未発送’;”
‘ 実行(これだけでトランザクションも内部で最適化されます)
CurrentDb.Execute sql, dbFailOnError
MsgBox “SQLによる一括更新が完了しました!”, vbInformation
End Sub
「じゃあ、レコードセット意味ないじゃん!」と思われるかもしれませんが、そんなことはありません。
- SQL一撃:単純な条件の一括変更や削除に向いている。
- DAOレコードセット + トランザクション:1件ずつ複雑な計算を挟んだり、入力チェックを行ったりしながら安全に処理したいときに向いている。
この2つを適材適所で使い分けられるようになると、あなたのAccess開発スキルは中級者から一気に「上級者」へと駆け上がります。
—
まとめ
今回は、DAOのトランザクション管理を活用した高速な一括更新について解説しました。
- 大量データ処理は、トランザクション(`BeginTrans` / `CommitTrans`)で囲んでディスクへの書き込み回数を減らす。
- エラー時の `Rollback` を仕込んでデータの整合性を守る。
- 処理が終わったら、必ずオブジェクトを解放する。
ここをクリアすれば、もう「動くけれど遅いプログラム」に悩まされることはありません。
ぜひ、実際の開発環境で試してみてくださいね。あなたのAccessライフがより快適で楽しいものになることを応援しています!
