【入門編】QueryDefの実行時間を計測し、遅いSQLを自動特定する「パフォーマンスプロファイラ」の自作 – Access VBA解析バイブル

スポンサーリンク

こんにちは! Access VBAの開発現場で、日々「なんだか最近、この画面を開くのがやたらと遅いな……」と頭を抱えていませんか?

マクロの記録から一歩踏み出し、自力でコードを書けるようになったあなたへ。今回は、Accessのパフォーマンスチューニングにおいて最も強力な武器となる「QueryDefを使ったパフォーマンスプロファイラ(実行時間計測ツール)」の作り方を、魂を込めて伝授します。

ここをクリアすれば、単に「動くコードを書く人」から、システム全体の挙動を支配する「真のエンジニア」へとステップアップできますよ。ぜひ最後までついてきてくださいね。

—

なぜ、あなたのAccessアプリは遅くなるのか?

Access(Jet/ACEデータベースエンジン)で開発していると、データ量が増えた途端に処理が重くなる現象に直面します。その大半の原因は、「どのSQLがボトルネック(遅延の原因)になっているか分からない」という点にあります。

なんとなく「このクエリが怪しい気がする……」と勘で修正しても、それはエンジニアの仕事ではありません。データに基づき、どのSQLが何秒かかっているのかを「可視化」する仕組み、それが今回作るパフォーマンスプロファイラです。

—

秘伝の武器:QueryDefオブジェクトとは?

Access VBAでSQLを実行する方法には、`DoCmd.RunSQL` や `CurrentDb.Execute` などがありますが、プロフェッショナルが使うのは`QueryDef`(クエリ定義)オブジェクトです。

従来の「その場しのぎSQL」と何が違うのか?

  • 通常実行: SQL文を毎回テキストとしてデータベースエンジンに渡し、エンジン側で「どうやってデータを取ろうか(実行計画)」を毎回イチから考えさせています。これでは無駄なオーバーヘッド(処理の負荷)が発生します。
  • QueryDef実行: あらかじめデータベースにSQLを「名前付きの設計図」として登録(またはコード内でコンパイル)するため、データベースエンジンが実行計画を最適化・記憶し、圧倒的な高速化と安全性の向上をもたらします。

この `QueryDef` のライフサイクル(生成・実行・破棄)を逆手に取り、実行時間をミリ秒単位で計測するのが今回のキモです。

—

実装!「QueryDefパフォーマンスプロファイラ」

それでは、実際に開発環境に組み込める実用コードを公開します。
標準モジュールを一つ新規作成し、以下のコードをそのまま貼り付けてみてください。

Option Explicit
‘ =================================================================
‘ 模块名: modQueryProfiler
‘ 概要: QueryDefの実行時間を計測・ログ出力するプロファイラ
‘ =================================================================

Public Sub RunProfiledQuery(ByVal strQueryName As String, Optional ByVal isDebugPrint As Boolean = True)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim startTime As Double
Dim elapsedTime As Double

‘ エラーハンドリングの要塞
On Error GoTo ErrorHandler

Set db = CurrentDb

‘ 1. QueryDefオブジェクトの取得(ここで存在チェックも兼ねる)
Set qdf = db.QueryDefs(strQueryName)

‘ 2. 実行前の時刻を高精度タイマー(Timer)で取得
startTime = Timer

‘ 3. クエリの実行(データ変更系の場合はExecute、参照系はOpenRecordsetなど用途に合わせて拡張可能)
‘ ここでは汎用的にSQLを実行またはレコードセットを開く負荷をシミュレート
‘ ※アクションクエリ(更新・追加・削除)を想定した実行例
qdf.Execute dbFailOnError

‘ 4. 実行後の経過時間を算出(秒単位、小数点以下も取得)
elapsedTime = Timer – startTime

‘ 5. 結果のログ出力(イミディエイトウィンドウ & 必要に応じてテーブル保存も可)
If isDebugPrint Then
Debug.Print “【プロファイル結果】” & _
” / クエリ名: ” & strQueryName & _
” / 実行時間: ” & Format(elapsedTime, “0.000”) & ” 秒” & _
” / 実行時刻: ” & Now
End If

‘ 6. クリーンアップ
Set qdf = Nothing
Set db = Nothing
Exit Sub

ErrorHandler:
‘ 予期せぬエラーをキャッチし、どのクエリで死んだかを明確にする
MsgBox “プロファイル中にエラーが発生しました。” & vbCrLf & _
“対象クエリ: ” & strQueryName & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“内容: ” & Err.Description, vbCritical, “プロファイラ異常終了”

Set qdf = Nothing
Set db = Nothing
End Sub

—

コードの急所を徹底解説!

初心者の方でも「なぜこの書き方をしているのか」が腑に落ちるよう、重要なポイントを紐解きます。

1. `Timer` 関数の活用

VBAの `Timer` 関数は、午前0時からの経過秒数を浮動小数点数(Double型)で返します。処理の「前」と「後」でこの値を引き算することで、ミリ秒単位(正確には10分の1秒〜数ミリ秒単位)の実行時間を導き出すことができます。
「なんとなく重い」を「0.842秒かかっている」という客観的な事実に変える魔法の関数です。

2. `dbFailOnError` というプロの流儀

`qdf.Execute` の後ろにしれっと書かれている `dbFailOnError`。これ、めちゃくちゃ重要です。
もし途中のデータ処理でエラー(重複エラーや型の不一致など)が起きたとき、このオプションがないと「途中まで変更されて、途中で止まる」というデータベースの最も恐ろしい状態(データの不整合)を引き起こします。これを指定しておけば、エラー時に自動で変更がロールバック(なかったことに)されます。必ずセットで覚えましょう。

3. オブジェクトの解放(クリーンアップ)

コードの最後にある `Set qdf = Nothing` と `Set db = Nothing`。
「めんどくさいから書かなくていいや」と省いていませんか? VBAが裏側で持っているメモリ領域(参照カウンタ)を綺麗に解放してあげないと、メモリリーク(メモリの食い潰し)を引き起こし、Access全体の動作が不安定になります。「使ったオブジェクトは自分で片付ける」。これが一流への第一歩です。

—

現場で陥りやすい「よくあるエラー」と対策

プロファイラを動かす際、初心者が必ずと言っていいほどつまずくポイントを先回りして解説します。

  • エラー「実行時エラー ‘3265’: 項目が見つかりませんでした。」
  • 原因: 指定したクエリ名(`strQueryName`)が、Accessのナビゲーションウィンドウに実在しない、またはスペルミスをしている。
  • 対策: クエリ名が正しいか、すでに削除されていないかを確認してください。また、一時的な動的SQLを測りたい場合は、あらかじめコード内で `db.CreateQueryDef` を使って一時クエリとして登録するアプローチに発展させると完璧です。

—

まとめ:ここをクリアすれば、Access VBAは怖くない!

お疲れ様でした! 今回は、単にコードを動かすだけでなく、「システムの健康状態を計測・監視するプロファイラ」という少し高度な仕組みに挑戦しました。

  • `Timer` 関数で処理時間を数値化する技術
  • `QueryDef` を使った堅牢で高速なSQL実行
  • `dbFailOnError` とオブジェクト解放による安全管理

これらをマスターしたあなたなら、もうマクロの記録の制限に縛られることはありません。現場で「この処理遅いな」と感じたら、ぜひこのプロファイラをサクッと組んで、ボトルネックを秒速で突き止めてみてください。

「ここをクリアすれば、Access VBAの基本はバッチリですよ!」
自信を持って、次の開発も楽しんでいきましょう!

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