【実務・中級編】フォームの「BeforeUpdate」イベントで実装する、複雑な入力値妥当性チェック – Access VBA解析バイブル

スポンサーリンク

Access VBAの深淵:BeforeUpdateで「絶対に不正データを許さない」堅牢なバリデーション設計

現場のAccess開発で最も忌むべきは、「不整合なデータがテーブルに書き込まれてしまうこと」だ。

未熟なエンジニアは、各コントロールの `AfterUpdate` イベントに個別のチェックを詰め込み、結果としてスパゲッティコードを生む。しかし、真のプロフェッショナルは知っている。データの一貫性を保証する最後の砦は、フォームの `BeforeUpdate` イベントただ一つであるということを。

今回は、業務システムにおける「複数フィールドの整合性チェック」を、保守性と堅牢性を両立させて実装するアーキテクチャを伝授する。

1. なぜ「個別のコントロール」でチェックしてはいけないのか

各コントロールの `AfterUpdate` でチェックを行うと、以下の問題が発生する。

  • 順序依存の罠: Aフィールドを入力した時点では正しいが、Bフィールドとの組み合わせで不正になる場合、バリデーションが破綻する。
  • バイパスの脆弱性: ユーザーがデータを修正せず、直接「保存ボタン(または閉じるボタン)」を押した場合、個別の `AfterUpdate` は発火しない。
  • 保守性の欠如: バリデーションルールが変わるたび、フォーム上のあちこちのコードを書き換える羽目になる。

「保存の直前(BeforeUpdate)」こそが、データの生殺与奪の権を握る唯一の場所だ。

2. プロダクション品質のBeforeUpdate実装パターン

以下のコードは、単なる入力チェックではない。エラー発生時の「ユーザー体験」と「キャンセル処理」を完全に制御する設計だ。

Private Sub Form_BeforeUpdate(Cancel As Integer)
‘ 目的: フォーム全体のデータ保存直前に整合性チェックを行う
‘ Cancel = True に設定することで、保存プロセスを強制停止する

On Error GoTo Err_Handler

‘ 1. 基本的な必須項目チェック
If IsNull(Me.txt_OrderDate) Or IsNull(Me.txt_CustomerID) Then
MsgBox “必須項目が不足しています。”, vbCritical, “検証エラー”
Cancel = True
Me.txt_OrderDate.SetFocus
Exit Sub
End If

‘ 2. 複数フィールドにまたがるロジックチェック
‘ 例: 納品日は受注日より後でなければならない
If Not IsNull(Me.txt_DeliveryDate) Then
If Me.txt_DeliveryDate < Me.txt_OrderDate Then MsgBox "納品日は受注日以降の日付を入力してください。", vbExclamation, "論理整合性エラー" Cancel = True Me.txt_DeliveryDate.SetFocus Exit Sub End If End If ' 3. データベース連携を伴う複雑なチェック(CurrentDbの活用) ' 必要に応じて、既存レコードとの重複や予算上限チェック等をここで行う If Not IsValidBudget(Me.txt_CustomerID, Me.txt_Amount) Then MsgBox "予算上限を超過しています。", vbCritical, "業務ルール違反" Cancel = True Exit Sub End If Exit Sub Err_Handler: MsgBox "予期せぬエラーが発生しました: " & Err.Description, vbCritical Cancel = True End Sub ' 業務ロジックは別関数に切り出すのが鉄則 Private Function IsValidBudget(CustomerID As Long, Amount As Currency) As Boolean Dim rs As DAO.Recordset ' CurrentDb.OpenRecordset はキャッシュに注意。最新情報を取得する場合の最適解 Set rs = CurrentDb.OpenRecordset("SELECT BudgetLimit FROM T_Customers WHERE ID = " & CustomerID) If Not rs.EOF Then IsValidBudget = (Amount <= rs!BudgetLimit) End If rs.Close Set rs = Nothing End Function ---

3. この設計が「堅牢」である理由

① キャンセル権の掌握

`Cancel = True` を発行することで、Accessの標準的な保存トランザクションを即座にロールバックさせる。データ不整合がテーブルに書き込まれる余地を物理的に断つ設計だ。

② SetFocusによるUXの最適化

エラー時に「どこが間違っているか」をユーザーに教えるだけでなく、該当コントロールへカーソルを飛ばすことで、ユーザーのストレスを最小限に抑える。

③ ロジックの疎結合化

`IsValidBudget` のように、複雑なチェックは専用の関数(またはクラスモジュール)に逃がす。これにより、将来的にバリデーションルールが変更されても、フォーム側のコードを汚さずに対応できる。

④ CurrentDbの賢い利用

`CurrentDb` は非常に強力だが、ループ内で頻繁に呼び出すとパフォーマンスを劣化させる。今回の例のように、保存直前の「ここぞ」というタイミングでのみ使用するのが、Accessエンジニアとしての正しい作法だ。

最後に:エンジニアへの提言

Accessは「誰でも作れるツール」であるがゆえに、粗製乱造されやすい環境でもある。しかし、「データの整合性を担保する」という責任において、プロのエンジニアとアマチュアには決定的な差が出る。

`BeforeUpdate` を制する者は、Accessのデータ整合性を制する。今日から君のフォームのバリデーションを、この「ラスト・ゲートキーパー」モデルに書き換えてほしい。コードが整理されるだけでなく、バグという名の不気味なノイズが驚くほど消滅するはずだ。

さあ、設計の質を一段階引き上げよう。現場は、君の「止まらないシステム」を待っている。

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