Accessの「クエリが日に日に遅くなる」現象を根絶する:QueryDefの実行計画を強制再構築する極意
Access開発の現場で、ある日突然「以前は一瞬で終わった処理が、なぜか数秒かかるようになった」という相談を受けることはないだろうか。
多くの初心者はこれを「データベースの肥大化」や「インデックス不足」のせいにする。もちろんそれらも要因だが、真の原因は別の場所にある。Access(Jet/ACEエンジン)が保持する「クエリの実行計画(Execution Plan)」の腐敗だ。
今回は、QueryDefを単なる「SQLの保管場所」ではなく、「常に最適化された実行エンジン」として制御するための、プロフェッショナルな設計手法を伝授する。
—
なぜQueryDefは「鮮度」が落ちるのか
Accessのクエリは、初めて実行された際に最適化が行われ、その実行計画がキャッシュされる。しかし、データ量が増減したり、インデックスの統計情報が古くなったりしても、一度生成された計画はすぐには破棄されない。
結果として、エンジンは「過去の最適解」に固執し、現在のデータセットに対しては「最悪の実行ルート」を歩み続ける。これを打破するためには、VBAからQueryDefのプロパティを再定義し、内部的な再コンパイルを強制する設計が必要だ。
堅牢なQueryDef更新・実行の実装パターン
ただSQLを書き換えるだけでは不十分だ。リソースを解放し、明示的にエンジンへ「新しいSQLだ」と認識させる手順を踏む必要がある。
以下に、実務でそのまま利用できる「QueryDef動的更新クラス(またはモジュール)」のコアロジックを提示する。
‘ ———————————————————————-
‘ プロシージャ名:ExecuteOptimizedQuery
‘ 目的:QueryDefの実行計画を強制再構築してSQLを実行する
‘ ———————————————————————-
Public Sub ExecuteOptimizedQuery(queryName As String, newSql As String)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Set db = CurrentDb
‘ 1. QueryDefオブジェクトの取得
‘ 存在しない場合は作成し、存在する場合は参照する
On Error Resume Next
Set qdf = db.QueryDefs(queryName)
If Err.Number <> 0 Then
Set qdf = db.CreateQueryDef(queryName)
End If
On Error GoTo 0
‘ 2. 重要:SQLを入れ替える前に一旦クリアする
‘ これにより、前回の実行計画との依存関係を断ち切る
qdf.SQL = “”
‘ 3. 新しいSQLを代入(ここで再コンパイルがトリガーされる)
qdf.SQL = newSql
‘ 4. パラメータの解決
‘ 必要に応じてここで qdf.Parameters(“…”) = value を設定する
‘ 5. 実行(アクションクエリの場合)
‘ dbFailOnErrorを指定し、実行時エラーを確実にトラップする
qdf.Execute dbFailOnError
‘ 6. クリーンアップ
qdf.Close
Set qdf = Nothing
Set db = Nothing
End Sub
この設計の「プロのこだわり」
1. `qdf.SQL = “”` という儀式
多くのエンジニアは古いSQLの上に新しいSQLを上書きするが、これではエンジンが差分更新を試みてしまい、最適化が不完全になるケースがある。一度空文字を入れることで、エンジンに対し「以前の計画は無効である」という明確なシグナルを送る。これが「再コンパイルの強制」の鍵だ。
2. `dbFailOnError` の徹底
VBAでクエリを実行する際、このオプションを省略してはならない。これがないと、SQLの構文エラーや主キー違反が発生しても黙殺され、後続の処理で不可解なバグを生むことになる。異常系を「即座に検知する」ことは、堅牢なシステムの絶対条件だ。
3. パラメータクエリの活用
今回紹介した手法は、`SELECT FROM TBL WHERE ID = [p1]` のようなパラメータクエリと組み合わせると真価を発揮する。`qdf.Parameters` を通じて値を注入することで、SQLインジェクションを防ぎつつ、実行プランの再利用性を高めることができる。
—
さらにパフォーマンスを突き詰めるには
もし、この手法をとってもなお速度改善が見られない場合は、以下のチェックリストを確認してほしい。
- 統計情報の更新: バックエンド(リンクテーブルの場合)のデータベースで、統計情報が最新か。SQL Serverがバックエンドなら、`sp_updatestats` を実行するだけで劇的に改善することがある。
- クエリの複雑性: `QueryDef` の中で別の `QueryDef` を呼び出す「入れ子」をやりすぎていないか。複雑な入れ子は実行計画の予測を極めて困難にする。中間テーブルを作るか、SQLをJOINでフラットに書き直す勇気を持とう。
- 暗黙の型変換: `WHERE` 句で、インデックスの貼られたフィールドと異なる型で比較していないか(例:数値型フィールドに文字列を渡す)。これはインデックスを無力化し、全件走査(フルテーブルスキャン)を招く最大の敵だ。
最後に:エンジニアとしての心構え
「コードを書いて動く」のはスタートラインに過ぎない。
「なぜ遅いのか」「なぜこの設計が安全なのか」を論理的に説明し、エンジン(Access)の挙動を支配下に置く。これこそが、ツールを作る担当者から「業務自動化のアーキテクト」へと進化するための境界線だ。
君たちが書くその一行のコードが、現場の時間を何時間削減するか。常にその重みを意識してほしい。
