Access VBAの「重い処理」でUIをフリーズさせない!QueryDef非同期実行の設計術
こんにちは。Accessの深淵へようこそ。
日々、Accessで「ボタンを押した途端、画面が真っ白になって固まる」という現象に頭を抱えていませんか?
Access VBAはシングルスレッドです。つまり、クエリの実行が完了するまで、Accessは「応答なし」の状態になり、ユーザーはただ祈りながら待つしかない。これは現代のアプリケーションとしては致命的な設計です。
今日は、「QueryDefを裏で回し、終わるまではUIを自由にする」という、中級者への登竜門となる非同期処理の極意を伝授します。
—
なぜ「非同期」が必要なのか?
Accessの`DoCmd.OpenQuery`や`CurrentDb.Execute`は、処理が終わるまで次のコードに進みません。これが「フリーズ」の正体です。
我々が目指すのは、以下のスマートな挙動です。
1. ボタンを押す。
2. 「処理中…」と表示し、進捗を示す。
3. 裏側でクエリが走っている間も、マウスは動かせるし、他の操作もできる。
4. 処理が終わった瞬間に、イベントで「完了しました!」と通知を受ける。
これを実現するために、DAOの`QueryDef`とフォームの`タイマーイベント`を組み合わせた「疑似非同期」の設計を採用します。
—
実装の設計図
今回は「重いクエリ」をバックグラウンドで投げ、タイマーを使って「終わったかどうか」を監視し続けるというアプローチをとります。
1. QueryDefの準備(SQLの動的構築)
まずは、実行するクエリを`QueryDef`として定義します。固定のSQLではなく、パラメーターを埋め込むことで柔軟性を高めます。
‘ 重いクエリを定義する関数
Public Sub PrepareQuery(ByVal paramValue As String)
Dim qdf As DAO.QueryDef
Set qdf = CurrentDb.QueryDefs(“qsel_HeavyProcess”)
‘ パラメーターを動的に書き換える
qdf.Parameters(“[Forms]![frmMain]![txtTarget]”) = paramValue
‘ これ以降、qdf.Execute は使わず、別の場所で実行状態を管理します
End Sub
2. 非同期実行のエンジン(タイマーの活用)
ここが肝です。フォームの `TimerInterval` プロパティを使い、定期的に「処理が終わったか?」をチェックします。
‘ フォームのモジュールに記述
Private Sub cmdStart_Click()
‘ 1. クエリ実行用のDAOオブジェクトを準備
Dim qdf As DAO.QueryDef
Set qdf = CurrentDb.QueryDefs(“qsel_HeavyProcess”)
‘ 2. フォームにタイマーを仕掛ける (100ミリ秒ごとに監視)
Me.TimerInterval = 100
‘ ここで非同期実行(非同期用のAPIを使わない簡易手法)
‘ 実務ではバックグラウンドプロセスを叩くのが理想ですが、
‘ Access単体では「非同期クエリ実行」をシミュレートします。
Debug.Print “処理開始: ” & Now
End Sub
Private Sub Form_Timer()
‘ タイマーイベントで「処理が完了したか」をポーリング(監視)
‘ ※実際には非同期で走らせるために、別途シェル実行などを組み合わせます
‘ 今回は簡易的に「処理中であることをユーザーに示す」役割を担います
Me.lblStatus.Caption = “処理中… ” & Format(Now, “ss”) & “秒経過”
‘ もし処理が完了したら…
If IsProcessFinished() Then
Me.TimerInterval = 0 ‘ タイマーを止める
Me.lblStatus.Caption = “完了しました!”
MsgBox “処理が終了しました。”
End If
End Sub
—
陥りやすい罠と解決策
Q1: 「非同期」と言っても、結局Accessは止まりませんか?
鋭いですね。Access単体でクエリを投げると、どうしてもエンジンがロックされます。本格的な非同期を実現するには、「別インスタンスのAccessで処理を走らせる」というテクニックが現場の定石です。
- 解決策: `Shell`関数を使って、別のAccessファイルをサイレント起動させ、そこでクエリを叩く。終わったらフラグ用のテキストファイルを書き込み、メインのAccessがそれを監視する。これが最もフリーズしない「大人の設計」です。
Q2: クエリの実行プランは最適化されていますか?
コード以前の問題として、インデックスが貼られていないテーブルに対して重いクエリを投げていませんか?
- 鉄則: `QueryDef`でSQLを投げる前に、`CurrentDb.TableDefs`を使ってインデックスの有無を確認するルーチンを組み込みましょう。
—
先輩エンジニアからのアドバイス
「Accessは遅い」と嘆く前に、「自分がAccessに何をさせているか」を可視化してください。
1. SQLの解析: `QueryDef`のSQLをSSMS(SQL Server Management Studio)や実行計画ツールに流し込み、どこでボトルネックが発生しているか突き止める。
2. UIの分離: ユーザーを待たせるなら、プログレスバーを実装する。これだけで「壊れているのか、動いているのか」の不安を取り除けます。
Access VBAは、正しく扱えば強力な武器になります。フリーズに立ち向かうその設計思考こそが、あなたを「マクロを作る人」から「システムを設計するエンジニア」へと進化させるはずです。
さあ、次はあなたの番です。まずは`TimerInterval`でUIを制御するところから始めてみてください。ここをクリアすれば、Accessの挙動はあなたの意のままです。応援していますよ。
