【実務・中級編】DoCmd.RunCommand acCmdSaveRecordの強制実行による入力値の確定とバリデーション – Access VBA解析バイブル

スポンサーリンク

【Access VBA極限知見】`acCmdSaveRecord`によるレコード強制確定と、バグを根絶するバリデーション設計

開発現場でこんな悲劇を見たことはないだろうか。
「ユーザーがフォームに適当な値を入力し、そのまま別のタブに移動したり閉じるボタンを押したりした。しかし、データが保存されていない、あるいは不整合な状態で中途半端にコミットされてしまった……」

Access VBAにおけるフォームのデータ更新は、実に気まぐれだ。フォーカスがコントロール上にある状態では、バッファにある入力値はまだ「宙に浮いている」。この状態でVBAから直接テーブルや別クエリを叩きにいったり、独自のバリデーションチェックを走らせたりすると、「画面に見えている値」と「データベースにある値」が乖離する致命的なバグを引き起こす。

今回は、このAccess特有のデータライフサイクルの罠を完全にハックし、実務で絶対に破綻しない「レコードの強制確定と堅牢なバリデーション設計」を伝授する。

1. なぜ「自然な保存」に頼ってはいけないのか?

Accessは、ユーザーがレコード移動を行ったり、フォームを閉じようとしたり、明示的に保存ボタンを押したりしたタイミングで、初めて以下のイベントチェーンを走らせる。

1. BeforeUpdate(更新前)イベント:データの検証を行う最後の砦
2. AfterUpdate(更新後)イベント:保存完了後の処理
3. Current(カレント)イベント:レコード移動時の処理

ここで問題になるのが、「VBAのコード側から、この保存のタイミングを完全にコントロールしたい」という実務上の要請だ。例えば、「登録ボタン」を押した瞬間にバリデーションを行い、OKなら保存、NGなら処理を中断したいとする。

この時、`Me.Dirty = False` と記述して保存を促す手法もあるが、エラーハンドリングやフォーカスの制御において挙動が不安定になるケースが多い。そこでおすすめするのが、Accessのコマンドを実行して強制的にバッファをフラッシュする手法である。

‘ レコードの強制保存を実行する
DoCmd.RunCommand acCmdSaveRecord

この1行を適切なタイミングで挟むことで、「画面上の入力値を強制的にJet/ACEエンジンに送り込み、正規の更新プロセスを強制発動させる」ことができる。

2. 現場で使える!堅牢なプロダクションコード例

実際の業務システムを想定した、ボタンクリック時の保存&バリデーションの模範実装を示す。
単に保存するだけでなく、入力漏れの検知、トランザクション的な思考、そしてエラーハンドリングまでを網羅したプロのコードだ。

Option Compare Database
Option Explicit

Private Sub btnSave_Click()
On Error GoTo ErrorHandler

Dim lngErrCount As Long
lngErrCount = 0

‘ —————————————————————-
‘ STEP 1: VBA側での簡易プレチェック(UI上の必須入力など)
‘ —————————————————————-
If IsNull(Me.txtCustomerName) Or Trim(Me.txtCustomerName & “”) = “” Then
MsgBox “顧客名は必須入力です。”, vbExclamation, “入力エラー”
Me.txtCustomerName.SetFocus
Exit Sub
End If

‘ —————————————————————-
‘ STEP 2: レコードの強制確定(ここでBeforeUpdateイベントが必ず走る)
‘ —————————————————————-
‘ 入力バッファをテーブルに書き込み、データの不整合を防ぐ
If Me.Dirty Then
DoCmd.RunCommand acCmdSaveRecord
End If

‘ —————————————————————-
‘ STEP 3: 保存後の後続処理(採番や関連テーブルへの展開など)
‘ —————————————————————-
MsgBox “データの保存が正常に完了しました。”, vbInformation, “処理成功”

Exit Sub

ErrorHandler:
‘ エラー番号 2501 は DoCmd のキャンセル等で発生するため除外またはハンドリング
If Err.Number = 2501 Then
‘ ユーザーまたは BeforeUpdate イベント内で保存がキャンセルされた場合
Exit Sub
End If

MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”
End Sub

このコードのアーキテクチャ上のポイント

1. `Me.Dirty` の事前チェック
変更がない状態で無駄に `acCmdSaveRecord` を叩くと、変更がない旨のエラーや余計なイベント発生を招くため、ダーティーフラグが立っている場合のみ実行する。
2. `DoCmd.RunCommand acCmdSaveRecord` の強制力
これを実行した瞬間、Accessは自動的に当該フォームの `BeforeUpdate` イベントを呼び出す。したがって、複雑なビジネスロジックやテーブルレベルの制約チェックは、フォームの `BeforeUpdate` イベント側に集約するのが最も美しい設計となる。

3. フォームの `BeforeUpdate` イベントと組み合わせた最強のバリデーション

前述の通り、`acCmdSaveRecord` を実行すると必ずフォームの `BeforeUpdate` イベントが発火する。こここそが、データ不整合を防ぐための要塞である。

以下のように `Cancel` 引数を利用して、バリデーションNGのときは保存を物理的に阻止する。

Private Sub Form_BeforeUpdate(Cancel As Integer)
‘ 業務ロジックに基づくバリデーション
If Me.txtOrderDate > Date Then
MsgBox “未来の日付を発注日に指定することはできません。”, vbCritical, “バリデーションエラー”

‘ 保存をキャンセルし、フォーカスを戻す
Cancel = True
Me.txtOrderDate.SetFocus
Exit Sub
End If

‘ 数量と単価の整合性チェック
If Me.txtQuantity <= 0 Then MsgBox "数量は1以上を指定してください。", vbCritical, "バリデーションエラー" Cancel = True Me.txtQuantity.SetFocus Exit Sub End If End Sub この設計にしておけば、ユーザーがどのルート(レコード移動、閉じるボタン、自作の保存ボタン)でレコードを抜けようとしても、`BeforeUpdate` が必ずガードするため、「保存漏れデータがデータベースに紛れ込む」というバグを完全にゼロにすることが可能だ。

チーフアーキテクトからの総括

Access VBAにおける最大の悪手は、「イベントの走る順番やタイミングを神様にお祈りしながらコードを書くこと」である。

データがどこに存在し(UIのバッファか、テーブルのストレージか)、いつ確定されるべきかをプログラマが完全に支配下におくこと。そのための強力な武器が `DoCmd.RunCommand acCmdSaveRecord` であり、それを起点としたイベント駆動型のバリデーション設計だ。

この構造をあなたのアプリケーションに導入すれば、データ起因の問い合わせやバグは劇的に激減するはずだ。プロフェッショナルとして、妥協のない堅牢なコードベースを築き上げてほしい。

タイトルとURLをコピーしました