【入門編】QueryDefの実行結果を「非同期」で取得する:重いクエリ実行中にUIをフリーズさせない工夫 – Access VBA解析バイブル

スポンサーリンク

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の挙動はあなたの意のままです。応援していますよ。

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