DoCmd.RunSQLとCurrentDb.Executeの使い分け:トランザクション制御の決定版
開発現場でよく見かける光景がある。「とりあえず動くから」と `DoCmd.RunSQL` を乱用し、エラーハンドリングは `On Error Resume Next` で力技でねじ伏せる。その結果、原因不明のデータ不整合に頭を抱え、夜遅くまでログと格闘する――。
プロとして断言しよう。その設計では、いつか必ずシステムが致命的な崩壊を起こす。
Access VBAにおけるデータ操作(Action Query)の実行方法は、主に2つ存在する。UI層の命令である `DoCmd.RunSQL` と、データアクセスエンジンの直接操作である `CurrentDb.Execute` だ。
この2者の違いの本質を理解し、トランザクション制御と組み合わせることで初めて、商用レベルの堅牢なアプリケーションが完成する。
今回は、現場のエンジニアが知るべき「真の使い分け」と、実務でそのまま使える堅牢な実装パターンを授けよう。
—
1. なぜ `DoCmd.RunSQL` は実務で使ってはいけないのか?
多くの入門書では、SQLを実行する手段として `DoCmd.RunSQL` が紹介される。しかし、プロのアーキテクトの視点から言えば、このメソッドには実務において致命的な欠陥がいくつか存在する。
① UI(画面)への依存と暗黙のオーバーヘッド
`DoCmd` オブジェクトは、本来マクロのコマンドをVBAから実行するための「UI層の代行者」だ。裏側でAccessの画面描画エンジンや警告ダイアログの制御を挟むため、純粋なデータ処理としてはオーバーヘッドが大きい。
② 警告抑制の副作用(`SetWarnings` の呪縛)
`DoCmd.RunSQL` でデータを更新すると、デフォルトでは「○件のレコードを追加します」といった確認ダイアログが表示される。これを消すために、多くの開発者は以下のようなコードを書く。
‘ 悪夢のアンチパターン
DoCmd.SetWarnings False
DoCmd.RunSQL “UPDATE T_Mutter SET … ”
DoCmd.SetWarnings True
この実装の何が問題か? 万が一、途中で予期せぬエラーが発生してコードが中断した場合、警告が「オフ」のまま画面が放置される。 ユーザーがそのあと手動でデータを操作した際、確認なしにデータが書き換えられるという大惨事を引き起こす。これが「バグの温床」と呼ばれる所以だ。
③ エラー情報の取得の困難さ
`DoCmd.RunSQL` では、クエリの実行結果(影響を受けたレコード数など)をプログラム側でスマートに取得することが困難である。
—
2. 決定版:`CurrentDb.Execute` とトランザクションの優位性
対して、DAO(Data Access Objects)をベースにした `CurrentDb.Execute` は、データベースエンジンを直接叩く。
- 警告の自動抑制: デフォルトで確認ダイアログは表示されない(`SetWarnings` は不要)。
- エラーの厳密な捕捉: `dbFailOnError` オプションを付与することで、クエリ実行時にエラーが発生した際、即座にVBAのエラーオブジェクト(Err)に処理を移せる。
- トランザクションの完全な制御: `Workspace` を介したトランザクションと組み合わせることで、複数テーブルへの一括更新で「すべて成功するか、すべてロールバック(無かったことに)するか」を完全に保証できる。
—
3. 【プロダクションコード】堅牢なトランザクション実装例
百聞は一見に如かず。実務で求められる「確実なロールバック」と「エラーハンドリング」を実装した、そのままコピペして使えるプロ仕様のモジュールを提示する。
このコードは、親テーブルと子テーブルにデータを同時に書き込み、途中でエラーが起きた場合に完全にデータを巻き戻す(ロールバックする)処理の模範解答だ。
Option Compare Database
Option Explicit
‘ ==============================================================================
‘ 処理名 : 注文データ登録トランザクションサンプル
‘ 概要 : 複数テーブルへの一括更新をトランザクション制御で安全に実行する
‘ ==============================================================================
Public Sub RegisterOrderWithTransaction()
Dim ws As DAO.Workspace
Dim db As DAO.Database
Dim strSQL1 As String
Dim strSQL2 As String
‘ エラーハンドラーの有効化
On Error GoTo ErrorHandler
‘ デフォルトのワークスペースを取得
Set ws = DBEngine.Workspaces(0)
‘ トランザクションの開始(これ以降の変更は確定するまで一時保留される)
ws.BeginTrans
‘ 現在のデータベース参照を取得
Set db = CurrentDb()
‘ 1. 親テーブルへのデータ追加(例:受注マスター)
strSQL1 = “INSERT INTO T_OrderMaster (OrderDate, CustomerID) ” & _
“VALUES (#2023-10-25#, 1001);”
‘ dbFailOnError を必ず指定し、エラー時に即座に例外を発生させる
db.Execute strSQL1, dbFailOnError
‘ ※もしここで何らかのビジネスロジックエラー(例:在庫切れなど)を意図的に起こす場合:
‘ Err.Raise 9999, “BusinessLogic”, “在庫が不足しています。”
‘ 2. 子テーブルへのデータ追加(例:受注明細)
‘ ※もしここで外部キー制約違反やデータ型不一致が起きると自動的にエラーとなる
strSQL2 = “INSERT INTO T_OrderDetail (OrderID, ItemCode, Quantity) ” & _
“VALUES (@@IDENTITY, ‘P-001’, 5);”
db.Execute strSQL2, dbFailOnError
‘ 全ての処理が成功した場合のみ、トランザクションを確定(コミット)する
ws.CommitTrans
MsgBox “データの登録が正常に完了しました。”, vbInformation, “処理成功”
GoTo Finally
ErrorHandler:
‘ 異常発生時のロールバック(変更をすべて破棄)
If Not ws Is Nothing Then
ws.Rollback
End If
‘ ユーザーフレンドリーなエラー通知と、デバッグ用の詳細出力
MsgBox “データの登録中にエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“内容: ” & Err.Description, vbCritical, “処理中断(ロールバック実行)”
Finally:
‘ オブジェクトの解放(メモリリークの防止)
Set db = Nothing
Set ws = Nothing
End Sub
コードのアーキテクチャ的解説
1. `DBEngine.Workspaces(0).BeginTrans / CommitTrans / Rollback`
Access VBAにおけるトランザクション制御の王道。`CurrentDb` ではなく `Workspace` に対してトランザクションを張るのが、DAOの仕様における正しい作法である。
2. `dbFailOnError` オプションの徹底
これを忘れてはならない。`db.Execute` の第2引数にこれを渡すことで、SQL文の構文ミスやデータ型違反、制約違反があった場合に無視されず、確実に `Err` オブジェクトに捕捉されるようになる。
3. `@@IDENTITY` の活用(発展)
上記コードの `@@IDENTITY` は、直前の `INSERT` で自動採番されたオートナンバー型(ID)の親レコードのプライマリキーを取得するためのAccess特有の関数だ。子テーブルに親のIDを紐づける際、これを使えば一連の処理が安全に完結する。
—
4. 開発現場でありがちな落とし穴と注意点
ファイル共有環境(分割構成)における注意点
実務のAccessシステムは、多くの場合「バックエンド(データ専用のACCDB)」と「フロントエンド(UI・プログラム専用のACCDB)」に分割されている(いわゆるデータベース分割構成)。
`CurrentDb.Execute` はネットワークを介したバックエンドのテーブルに対してもトランザクションを有効に機能させる。
ただし、ネットワークの切断などハードウェア起因の不安定要素があるため、トランザクションの範囲(`BeginTrans` から `CommitTrans` まで)は、必要最小限のSQL実行文だけに絞るべきだ。トランザクション内で重いループ処理やファイル操作を挟むと、レコードロックの競合やパフォーマンス低下を引き起こす。
—
総括:プログラマの品格
「動けばいい」のコードは、開発者本人を数ヶ月後に苦しめることになり、システムを引き継いだ後任者を絶望させる。
`DoCmd.RunSQL` と `SetWarnings` の組み合わせは、今この瞬間から封印せよ。
`CurrentDb.Execute` と `dbFailOnError`、そして確実なトランザクション制御(`BeginTrans` / `Rollback`)。
この3つを標準装備として使いこなせた時、あなたの書くAccess VBAは、プロフェッショナル呼ぶにふさわしい「堅牢なシステム」へと昇華する。
