DAOのFindFirstか、ADOのFilterか。――検索エンジンの「重み」を知り、設計に魂を込める
業務システムの開発現場において、「動けばいい」という思考停止が最も恐ろしい。
特にAccess VBAを用いた開発において、データ量が増大した瞬間にシステムが沈黙する、あるいは極端にレスポンスが悪化する現象の多くは、エンジニアが「オブジェクトのライフサイクル」と「検索メカニズムのアーキテクチャ」を理解していないことに起因する。
今回のテーマは、中級者への登竜門とも言える「DAOのFindFirst」と「ADOのFilter」の使い分けだ。
単なる速度比較で終わらせるつもりはない。なぜそのメソッドが速いのか、なぜその設計が破綻を招くのか。プロフェッショナルとして、その判断基準をここに叩き込む。
1. アーキテクチャの本質的違い:ネイティブか、ラッパーか
まず、君たちが使っている二つの武器の「出自」を理解せよ。ここを間違えると、どんなにコードを書き換えてもパフォーマンスは改善しない。
DAO (Data Access Objects)
DAOは、Accessのエンジンである「ACE/Jetエンジン」と密結合したネイティブなオブジェクトだ。データベースのページ構造を直接、あるいは極めて近いレイヤーで操作する。
いわば、その土地の言葉を母国語として話す「地元住民」だ。ローカルの `.accdb` や `.mdb` を扱うなら、DAOの右に出る者はいない。
ADO (ActiveX Data Objects)
ADOは、あらゆるデータベース(SQL Server, Oracle, MySQL等)に共通のインターフェースを提供するための抽象化レイヤー(ラッパー)だ。
これは「外交官」だ。異なる国の言語(プロトコル)を仲介する能力には長けているが、ローカルな通信においては、その「翻訳プロセス」がオーバーヘッド(余計な負荷)となる。
2. FindFirst vs Filter:メカニズムの深淵
検索メソッドの動作原理を解剖する。
DAO.FindFirst:ポインタの移動
`FindFirst` は、レコードセット内を走査し、条件に合致する最初のレコードを見つけた瞬間、「カーソル(現在地を示すポインタ)」をその位置へ移動させるだけの操作だ。
インデックスが適切に貼られていれば、B-Tree構造を辿るだけで済むため、計算量は極めて小さく、メモリ消費も最小限で済む。
ADO.Filter:ビューの再構築
一方で、ADOの `Filter` プロパティは性質が全く異なる。
`Filter` を適用した瞬間、ADOは条件に合致するレコードを抽出した「新しいビュー(論理的なサブセット)」をメモリ上に構築しようとする。
つまり、検索ではなく「データの再定義」に近い。データ量が増えれば増えるほど、この「ビューの構築」に要するコストは指数関数的に増大する。
| 特徴 | DAO.FindFirst | ADO.Filter |
|---|---|---|
| 主な用途 | 特定の1件を高速に特定する | 条件に合うレコード群を抽出する |
| 動作原理 | カーソル(ポインタ)の移動 | 新しいレコードセット(ビュー)の作成 |
| インデックス効果 | 極めて高い | 限定的(フィルタリングに依存) |
| メモリ負荷 | 極めて低い | データ量に応じて増大する |
3. 実務における「黄金の判断基準」
設計者として、君たちは以下のフローチャートに従ってメソッドを選択すべきだ。
1. 接続先はどこか?
- ローカルのAccessファイル $\rightarrow$ DAOを優先せよ。
- 外部SQL Server等 $\rightarrow$ ADOを選択せよ。
2. 目的は何か?
- 「特定のIDを持つ1件」を探す $\rightarrow$ `FindFirst` (DAO) を使え。
- 「特定の条件に合う複数件」を一覧表示したい $\rightarrow$ SQLの `WHERE` 句を使え。
- *注意:ADOを使っている場合でも、`Filter` プロパティではなく、最初から `SELECT … WHERE …` で取得するのが鉄則だ。*
4. プロダクション品質のコード実装
では、実際の現場でそのまま使える、堅牢な実装例を示す。
単に動くだけではない。エラーハンドリング、オブジェクトの解放、そしてパフォーマンスを意識した設計だ。
【パターンA】DAOによる超高速な単一レコード検索
大量のデータから、特定のキーを持つレコードを瞬時に特定する際の標準的な実装。
''
' 指定したIDに基づいてレコードを高速に特定する
' @param db : 開いているDAO.Databaseオブジェクト
' @param targetID : 検索したい一意のID
' @return : 見つかった場合はTrue、見つからない場合はFalse
''
Public Function FindSingleRecordByID(ByRef db As DAO.Database, ByVal targetID As Long) As Boolean
Dim rs As DAO.Recordset
Dim strSQL As String
On Error GoTo ErrorHandler
' 1. 必要なフィールドのみを抽出するSQL(全フィールド取得は避ける)
strSQL = "SELECT ID, CustomerName FROM T_Customers"
Set rs = db.OpenRecordset(strSQL, dbOpenDynaset)
' 2. FindFirstによるポインタ移動
' ※ID列にインデックスが貼られていることが前提
rs.FindFirst "ID = " & targetID
If Not rs.NoMatch Then
' 見つかった場合の処理
Debug.Print "Found: " & rs![CustomerName]
FindSingleRecordByID = True
Else
' 見つからなかった場合
FindSingleRecordByID = False
End If
CleanUp:
' 3. オブジェクトの確実な解放(メモリリーク防止)
If Not rs Is Nothing Then
rs.Close
Set rs = Nothing
End If
Exit Function
ErrorHandler:
MsgBox "Error in FindSingleRecordByID: " & Err.Description, vbCritical
FindSingleRecordByID = False
Resume CleanUp
End Function
【パターンB】ADOによる外部DB連携(Filterは使わない)
外部サーバーからデータを取得する場合、`Filter` プロパティに頼るのは二流だ。サーバー側で絞り込みを行わせる(Server-side filtering)のが一流の設計だ。
''
' SQL Server等の外部DBから条件に合うデータを取得する
' @param connectionString : 接続文字列
' @param categoryID : フィルタリング条件
''
Public Sub FetchRemoteData(ByVal connectionString As String, ByVal categoryID As Integer)
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strSQL As String
On Error GoTo ErrorHandler
Set conn = New ADODB.Connection
conn.Open connectionString
' 【重要】Filterプロパティではなく、SQLのWHERE句でサーバー側に負荷を逃がす
' これにより、ネットワーク経由で転送されるデータ量を最小化する
strSQL = "SELECT OrderID, OrderDate FROM T_Orders WHERE CategoryID = " & categoryID
Set rs = New ADODB.Recordset
' CursorTypeは用途に応じて適切に選択(adOpenStatic, adOpenForwardOnly等)
rs.Open strSQL, conn, adOpenStatic, adLockReadOnly
Do Until rs.EOF
Debug.Print rs!OrderID, rs!OrderDate
rs.MoveNext
Loop
CleanUp:
' リソースの破棄
If Not rs Is Nothing Then
If rs.State = adStateOpen Then rs.Close
Set rs = Nothing
End If
If Not conn Is Nothing Then
If conn.State = adStateOpen Then conn.Close
Set conn = Nothing
End If
Exit Sub
ErrorHandler:
MsgBox "Critical Error in FetchRemoteData: " & Err.Description, vbCritical
Resume CleanUp
End Sub
5. 結論:エンジニアとしての矜持
`DAO.FindFirst` と `ADO.Filter`。
これらは単なる「検索メソッド」ではない。それぞれが、メモリ、CPU、ネットワーク帯域に対して異なる「コスト」を要求する、設計上の選択肢だ。
- ローカルの高速性を追求するなら DAO を選べ。
- データ量が増えることを予見し、インデックスを設計せよ。
- 外部接続なら ADO を使い、フィルタリングは SQL 側に任せよ。
「なんとなく動く」コードを書くのは今日で終わりにしろ。
アーキテクチャの意図を理解し、意図的にメソッドを選択できるエンジニアだけが、真に堅牢でスケーラブルなシステムを構築できるのだ。
