【入門編】DAO.QueryDefを利用した「非連結フォーム」へのデータバインド高速化テクニック – Access VBA解析バイブル

スポンサーリンク

こんにちは!Access VBAの開発現場で、日夜データと格闘お疲れ様です。

今回は、Access開発者が必ず直面する「フォームの表示が重い……」という壁を鮮やかにブチ破る、『DAO.QueryDef』と『非連結フォーム』を組み合わせた超高速化テクニックを伝授します。

「マクロの記録や、フォームのレコードソースに直接テーブルを指定する方法は卒業したけれど、データ量が増えると途端に動きがモッサリする……」
そんな悩みを持っているなら、ここをクリアすれば、あなたのAccess VBAスキルは間違いなくプロの領域に到達します。

今回は、難しい理論を優しく噛み砕きながら、現場でそのまま使える最強のコードと共にお届けしますね。

—

なぜ、普通のフォームはデータが増えると重くなるのか?

まずは敵を知ることから始めましょう。
通常、Accessのフォームを作る際、プロパティの「レコードソース」に重たいテーブルやクエリを直接指定しますよね。

これは、Access(正確にはデータベースエンジンであるJET/ACE)に「フォームが開いた瞬間から、全データの管理と描画を丸投げする」という状態です。数万件のレコードがあれば、Accessはメモリを大量に消費し、ネットワーク(共有サーバー上の.accdbファイル)を行ったり来たりしてフリーズ寸前になります。

救世主:非連結フォーム + QueryDef = 最速の美学

そこで登場するのが今回の主役、「非連結フォーム(データソースを持たないフォーム)」と「DAO.QueryDef(あらかじめコンパイルされた高速なクエリ定義)」のタッグです。

このアプローチの思想はこうです。
1. 重たいデータ処理は、データベースの頭脳であるQueryDefにすべて任せる。
2. 抽出された「必要な分だけのデータ」を、VBAのメモリ空間(配列やループ)にサクッと取り込む。
3. 料理された綺麗なデータだけを、非連結のフォームのコントロールに流し込む。

この仕組みをモノにすれば、どれだけ巨大なデータベースが相手でも、体感速度は驚異的な「一瞬」に変わります。

—

現場で使える!高速データバインド実装コード

それでは、百聞は一見にしかず。実際に非連結フォームへ高速にデータを流し込むVBAコードを見てみましょう。

前提として、フォームには `txtSearch`(検索値入力用テキストボックス)、`cmdSearch`(検索ボタン)、そしてリストビューや非連結のテキストボックス群があると想定してください。今回はシンプルに、検索結果をイミディエイトウインドウへの出力と、フォームへの高速流し込みを行う基本形を示します。

Option Compare Database
Option Explicit

‘ =====================================================================
‘ @Title: QueryDefを活用した非連結フォームの高速データバインド
‘ @Author: チーフアーキテクト
‘ =====================================================================
Private Sub cmdSearch_Click()
On Error GoTo ErrorHandler

Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim rs As DAO.Recordset

‘ 1. データベースオブジェクトの取得(現在の接続)
Set db = CurrentDb

‘ —————————————————————–
‘ 2. あらかじめ作成しておいたQueryDefの名前を指定して取得
‘ ※あらかじめデザインビューで「qryFastSearch」という名前の
‘ パラメータクエリを作っておくのがプロの技です。
‘ —————————————————————–
Set qdf = db.QueryDefs(“qryFastSearch”)

‘ 3. フォームから入力された検索条件をパラメータに安全にバインド
‘ (SQLインジェクション対策としても非常に重要です)
qdf.Parameters(“[Forms![frmMain![txtSearch”) = Me.txtSearch.Value

‘ 4. クエリを実行し、レコードセット(メモリ上の結果テーブル)に格納
‘ dbOpenSnapshot を使うことで、読み取り専用の軽量なメモリ領域を確保します
Set rs = qdf.OpenRecordset(dbOpenSnapshot)

‘ 5. データがゼロ件の場合のガード処理
If rs.EOF Then
MsgBox “該当するデータは見つかりませんでした。”, vbInformation, “検索結果”
GoTo Cleanup
End If

‘ 6. 【超高速化の真髄】
‘ レコードを配列に一度に一気読み(GetRowsメソッド)
‘ これにより、VBAとAccessエンジンの間の通信コストを極限まで削ぎ落とします。
Dim vntData As Variant
vntData = rs.GetRows() ‘ 2次元配列 (列, 行) に一括格納

‘ 7. 非連結フォームへデータを展開する処理
‘ (※ここでは分かりやすく概念を示しています。実際にはリストボックスの
‘ RowSourceTypeを「Value List」にして配列をぶち込むか、
‘ 画面上のコントロールにループで値を転記します)
Call BindDataToControls(vntData)

Cleanup:
‘ 8. 確実なオブジェクトの解放(メモリリークの完全阻止)
‘ これができないエンジニアは一流になれません。
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
If Not qdf Is Nothing Then Set qdf = Nothing
If Not db Is Nothing Then Set db = Nothing
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “システムエラー”
Resume Cleanup
End Sub

‘ ———————————————————————
‘ 補助プロシージャ:配列データをコントロールに流し込む
‘ ———————————————————————
Private Sub BindDataToControls(ByVal vntData As Variant)
Dim lngRow As Long
Dim lngCol As Long

‘ vntData(列, 行) という特殊なインデックス構造になっています
‘ 行数の上限を取得するには UBound(vntData, 2) を使います
lngRow = UBound(vntData, 2) + 1

Debug.WriteLine “取得レコード数: ” & lngRow & ” 件”

‘ ここで非連結コントロールへの代入処理を行います
‘ 例: Me.txtResultName.Value = vntData(1, 0) ‘ 0行目の1列目
End Sub

—

コードの深掘り解説:ここがプロの知見ポイント

上記のコードには、Access VBAのパフォーマンスを極限まで引き出すための「3つの絶対正義」が隠されています。

① `QueryDef` をあらかじめ作っておく理由

VBAのコード内で `CurrentDb.OpenRecordset(“SELECT FROM …”)` と直接SQL文字列を書く初心者が多いですが、これだとAccessは毎回「このSQLはどんな意味だ?」と構文解析(パース)からやり直します。
あらかじめクエリ定義(QueryDef)オブジェクトとしてAccess側に保存しておくことで、実行計画が最適化され、コンパイル済みの状態で秒速実行されます。

② `dbOpenSnapshot` と `GetRows` のコンボ

レコードセットを開く際、何も指定しないと更新可能な `dbOpenDynaset` になり、余計なロック管理のオーバーヘッドが発生します。検索・閲覧用なら `dbOpenSnapshot` 一択です。
さらに、`rs.GetRows()` を使ってVBA側の「配列(Variant型)」に一気にデータを引き抜くことで、レコードを1行ずつ `MoveNext` するループ処理と比べて、体感で10倍以上の速度差を生み出します。

③ オブジェクトの「完全解放」の美学

Access VBAで最も恐ろしいのは、見えないところでメモリが残留し、Accessファイルが肥大化・破損することです。
`Set rs = Nothing`、`Set qdf = Nothing`、`Set db = Nothing` の3点セットを必ず `Errorハンドラ` と `Cleanup` ラベルで担保する。この習慣こそが、あなたを「脱初心者」へと導く最大の武器になります。

—

陥りがちな罠とエラー回避の心得

このテクニックを実装する際、初心者が必ずハマる罠が1つあります。

> 「配列のインデックスの縦横が逆転してエラーになる!」

`GetRows()` で取得した配列は、私たちが普段エクセルなどで想像する `(行, 列)` ではなく、`(列, 行)` という順番で格納されます。
つまり、`vntData(0, 5)` は「0番目の列の、5行目のデータ」を指します。ここを間違えると「インデックスが有効範囲にありません」というエラーの餌食になりますので、必ず頭に叩き込んでおいてください。

—

まとめ

今回は、`DAO.QueryDef` と非連結フォームを組み合わせた、Access VBAの超高速化テクニックを解説しました。

  • 重たい処理はAccessのクエリ(QueryDef)に任せる
  • データは Snapshot で取得し、GetRows で配列に一気飲みする
  • 使い終わったオブジェクトは、責任を持ってキレイに片付ける

この基本原則を守るだけで、あなたの作るAccessアプリは見違えるほど軽快になり、ユーザーからの評価も爆発的に上がります。

ここをクリアすれば、Access VBAの基本はもうバッチリです!
ぜひ、今日の開発からあなたのプロジェクトに取り入れて、その圧倒的なスピードの違いを体感してみてください。エンジニアとしての新しい扉が、きっと開きますよ!

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