【テクニカル・上級編】DAO.QueryDefとADO.Commandの比較:Access内処理と外部DB連携の最適解 – Access VBA解析バイブル

スポンサーリンク

Accessの深淵:DAO.QueryDefとADO.Commandの真実。適材適所の「極限」

Access開発において、多くのエンジニアが陥る罠がある。「とりあえずDAOを使えばいい」あるいは「ADOの方が新しいから優れている」という短絡的な思考だ。

しかし、真のアーキテクトは知っている。Accessというデータベースエンジンが、どのようなメモリ管理を行い、どのタイミングでクエリをコンパイルし、バックエンドとの通信をどう裁いているのかを。今回は、DAO.QueryDefとADO.Commandの境界線、その「最適解」を深掘りする。

—

1. DAO.QueryDef:Access内部処理の「支配者」

DAO(Data Access Objects)は、Accessのネイティブな心臓部であるJet/ACEエンジンと直結している。

なぜQueryDefなのか

QueryDefオブジェクトを保存しておく最大の利点は、「実行計画の事前コンパイル」にある。Accessは一度実行されたクエリの実行計画をキャッシュし、2回目以降の実行速度を劇的に向上させる。

特に、動的SQLを構築する際、コード内で文字列を連結して`DoCmd.RunSQL`や`CurrentDb.Execute`を叩くのは愚策だ。メモリ上に無駄なクエリ定義を量産し、デコンパイルの効率を下げ、データベースの肥大化を招く。

極限の知見:動的パラメータークエリの再利用

動的SQLを組む際は、一時的にQueryDefを作成し、`Parameters`コレクションを操作せよ。

‘ DAOによる動的SQLの最適実行例
Public Sub ExecuteOptimizedQuery(ByVal targetID As Long)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef

Set db = CurrentDb
‘ 既存のクエリ定義を流用、または一時的に生成
Set qdf = db.CreateQueryDef(“”, “SELECT FROM T_Orders WHERE OrderID = [pID]”)

‘ パラメーターをバインド(型定義を明示することでエンジンへの負荷を軽減)
qdf.Parameters(“pID”) = targetID

Dim rs As DAO.Recordset
Set rs = qdf.OpenRecordset(dbOpenSnapshot)

‘ — 処理 —

‘ オブジェクトの明示的解放(VBAのGCを待たない)
rs.Close: Set rs = Nothing
qdf.Close: Set qdf = Nothing
Set db = Nothing
End Sub

—

2. ADO.Command:外部DB連携の「特使」

一方で、SQL ServerやAzure SQL Databaseなどの外部DBをバックエンドに据えた場合、DAOはもはや足枷となる。ここでの最適解は、間違いなく ADO (ActiveX Data Objects) だ。

なぜADOなのか

DAOがACEエンジンを介して外部DBと通信する場合、一度「Jetエンジンによる翻訳」というオーバーヘッドが発生する。対してADOは、OLE DBプロバイダ(MSOLEDBSQLなど)を介して直接SQLを流し込む。

特に`ADODB.Command`オブジェクトを使用し、`Prepared = True`とすることで、SQL Server側での実行プランのキャッシュを最大限に活かせる。

極限の知見:接続プーリングとタイムアウト制御

外部連携で最も重要なのは「接続の枯渇」と「ブロック」だ。以下のパターンを標準装備せよ。

‘ ADOによる外部SQL Server連携の最適例
Public Sub ExecuteExternalCommand(ByVal sql As String, ByVal paramValue As String)
Dim conn As ADODB.Connection
Dim cmd As ADODB.Command

Set conn = New ADODB.Connection
conn.ConnectionString = “Provider=MSOLEDBSQL;Server=…;Database=…;Trusted_Connection=yes;”
conn.Open

Set cmd = New ADODB.Command
With cmd
.ActiveConnection = conn
.CommandText = “UPDATE T_Log SET Status = ? WHERE LogID = ?”
.CommandType = adCmdText
.Prepared = True ‘ 実行計画をサーバー側でキャッシュさせる

‘ パラメーターの明示的指定(型不一致による暗黙の型変換を防ぐ)
.Parameters.Append .CreateParameter(“@Status”, adVarChar, adParamInput, 50, “Processed”)
.Parameters.Append .CreateParameter(“@ID”, adInteger, adParamInput, , paramValue)

.Execute
End With

conn.Close
Set cmd = Nothing: Set conn = Nothing
End Sub

—

3. シニアエンジニアが意識すべき「境界線」

現場のシステムを保守する際、以下のチェックリストを常に頭に置いてほしい。

1. 接続先はどこか?:

  • `.accdb`内部 → `DAO` (QueryDefで定義し、実行計画を固定せよ)
  • 外部RDBMS → `ADO` (Commandオブジェクトを使い、Preparedを意識せよ)

2. メモリ解放の義務:

  • `Set Object = Nothing`は単なる儀式ではない。特に長時間稼働するAccessフロントエンドでは、参照カウントをゼロにしなければ、メモリリークが確実に発生する。

3. Windows APIとの共存:

  • 大規模データ処理を行う際、`DoEvents`を乱用するな。代わりにWindows APIの`Sleep`や`MsgWaitForMultipleObjects`を用いて、CPUリソースと応答性のバランスを制御せよ。

結びに代えて

Accessは、もはや「初心者のための玩具」ではない。DAOとADOの特性を理解し、クエリのライフサイクルを制御できる者だけが、このレガシー環境で圧倒的なパフォーマンスを引き出すことができる。

コードは嘘をつかない。君が書いたその一行が、将来のデバッグ工数を決定づけることを忘れるな。明日の現場で、この知見が君の武器になることを期待する。

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