【入門編】DAOとADOのハイブリッド設計:ローカル一時テーブルを活用したデータ加工戦略 – Access VBA解析バイブル

スポンサーリンク
スポンサーリンク

Access VBAを掌握する極限の知見:DAOとADOのハイブリッド設計による「爆速」データ加工戦略

「プログラムを動かしてみたけれど、データの件数が増えた途端に、信じられないほど動作が重くなってしまった……」

もしあなたが今、そんな壁にぶつかっているとしたら、おめでとうございます。あなたは今、プログラミングの「初級者」から「実務を支えるエンジニア」へと脱皮するための、最も重要な境界線に立っています。

マクロの記録や、教科書通りの単純なコードでは、大量のデータを扱う「現場」のスピードには勝てません。なぜなら、コンピュータの処理速度そのものよりも、「データの移動距離(ネットワークの遅延)」と「オブジェクトの使い分け」こそが、パフォーマンスの真のボトルネックになるからです。

今日は、私が長年の設計現場で磨き上げてきた、DAO(Data Access Objects)とADO(ActiveX Data Objects)を使い分ける「ハイブリッド設計」という極意を伝授しましょう。

1. そもそも、なぜDAOとADOを使い分ける必要があるのか?

まず、混乱しやすいこの2つの登場人物を整理しましょう。彼らはどちらも「データベースを操作するための道具」ですが、その性格は全く異なります。

DAO:ローカルの「職人」

DAOは、Access(ACEエンジン)の内部構造に深く食い込んでいる、いわば「Access専用の職人」です。

  • 得意技: Access内のローカルテーブルに対する高速な読み書き、複雑なリレーションシップの操作。
  • 弱点: 外部のSQL Serverなどのネットワーク越しにあるデータベースを操作しようとすると、通信のオーバーヘッドで急激に速度が落ちることがあります。

ADO:ネットワークの「運び屋」

ADOは、異なる種類のデータベース(SQL Server, Oracle, Web APIなど)とやり取りするための、「汎用的な運び屋」です。

  • 得意技: 外部データベースへの接続、ネットワーク越しの大規模なデータ転送、強力なトランザクション制御。
  • 弱点: Access内部のテーブル操作においては、DAOに比べると一歩譲る(オーバーヘッドが大きい)場合があります。

結論:ハイブリッド戦略とは

「外部のデータを、ネットワーク越しに一つずつ処理するのは非効率。だから、一旦ローカル(Access内)にガサッと持ってきて、職人(DAO)に高速加工させ、最後に運び屋(ADO)を使って一気に書き戻す」

これが、実務で最も効率的なアーキテクチャなのです。

2. ハイブリッド設計の全体フロー

図解的にイメージすると、以下の4ステップになります。

1. 【抽出】 ADOを使って、外部DBから必要なデータを一括で取得する。
2. 【展開】 取得したデータを、Access内の「ローカル一時テーブル」に流し込む。
3. 【加工】 DAOを駆使して、ローカルテーブル上で複雑な計算や集計を爆速で行う。
4. 【反映】 加工済みのデータを、ADOを使って外部DBへ一括で書き戻す。

3. 実戦コード:ローカル一時テーブルを活用した加工戦略

それでは、実際に現場で使えるレベルのコードを見ていきましょう。
ここでは、「外部のSQL Serverから売上データを取得し、Access内で消費税計算などの加工を行い、結果をサーバーに戻す」というシナリオを想定します。

Option Compare Database
Option Explicit

' ======================================================================================
' テーマ:DAOとADOのハイブリッド設計による高速データ加工
' 概要:外部データをローカル一時テーブルへ集約し、DAOで加工後にADOで書き戻す
' ======================================================================================

Public Sub ExecuteHybridProcess()
    On Error GoTo ErrorHandler

    ' --- 変数宣言 ---
    Dim db As DAO.Database
    Dim rsLocal As DAO.Recordset
    Dim adoConn As ADODB.Connection
    Dim adoRs As ADODB.Recordset
    Dim strTempTableName As String
    Dim strConn As String
    
    ' 設定値(実際は設定ファイルや定数管理が望ましい)
    strTempTableName = "tbl_Work_Sales" ' ローカルの一時テーブル名
    strConn = "Provider=SQLNCLI11;Server=YourServer;Database=YourDB;UID=user;PWD=pass;"

    Set db = CurrentDb
    Set adoConn = New ADODB.Connection

    ' ----------------------------------------------------------------------------------
    ' STEP 1: 前準備(一時テーブルのクリーンアップ)
    ' ----------------------------------------------------------------------------------
    Debug.Print "Step 1: Cleaning up temporary table..."
    On Error Resume Next
    db.Execute "DELETE FROM " & strTempTableName, dbFailOnError
    On Error GoTo ErrorHandler

    ' ----------------------------------------------------------------------------------
    ' STEP 2: 【ADO】外部DBからデータを一括取得し、ローカルへ展開
    ' ----------------------------------------------------------------------------------
    Debug.Print "Step 2: Fetching data via ADO..."
    adoConn.Open strConn
    
    ' 外部DBからデータを取得するためのRecordset
    Set adoRs = New ADODB.Recordset
    adoRs.Open "SELECT SaleID, Amount, TaxRate FROM SalesTable WHERE Processed = 0", adoConn, adodbOpenForwardOnly, adodbLockReadOnly

    ' ローカルテーブルへ書き込み(DAOを使用)
    Set rsLocal = db.OpenRecordset(strTempTableName, dbOpenDynaset)
    
    Do Until adoRs.EOF
        rsLocal.AddNew
        rsLocal!SaleID = adoRs!SaleID
        rsLocal!Amount = adoRs!Amount
        rsLocal!TaxRate = adoRs!TaxRate
        rsLocal.Update
        adoRs.MoveNext
    Loop
    
    adoRs.Close
    Set adoRs = Nothing
    ' ----------------------------------------------------------------------------------

    ' ----------------------------------------------------------------------------------
    ' STEP 3: 【DAO】ローカルで高速なデータ加工
    ' ----------------------------------------------------------------------------------
    Debug.Print "Step 3: Processing data via DAO..."
    ' ここではDAOの強みを活かし、Recordsetをループして複雑な計算を行う
    ' ローカルテーブルなので、ネットワーク遅延を気にせず高速に回せる
    rsLocal.MoveFirst
    Do Until rsLocal.EOF
        ' 例:複雑な計算ロジック(本来はもっと重い処理を想定)
        Dim totalTax As Double
        totalTax = rsLocal!Amount * (rsLocal!TaxRate / 100)
        
        ' 加工結果を一時テーブルに書き戻す(フィールドが定義されている前提)
        rsLocal!TaxAmount = totalTax
        rsLocal!TotalAmount = rsLocal!Amount + totalTax
        rsLocal.Update
        
        rsLocal.MoveNext
    Loop
    rsLocal.Close
    ' ----------------------------------------------------------------------------------

    ' ----------------------------------------------------------------------------------
    ' STEP 4: 【ADO】加工済みデータを外部DBへ一括書き戻し(トランザクション管理)
    ' ----------------------------------------------------------------------------------
    Debug.Print "Step 4: Writing back to server via ADO..."
    
    ' 外部DBの整合性を守るため、ADOのトランザクションを開始
    adoConn.BeginTrans
    
    ' 再度ローカルデータを読み込む
    Set rsLocal = db.OpenRecordset(strTempTableName, dbOpenSnapshot)
    
    ' 外部DBへの更新用Recordset
    Set adoRs = New ADODB.Recordset
    adoRs.Open "SELECT SaleID, TaxAmount, TotalAmount FROM SalesTable WHERE 1=0", adoConn, adodbOpenKeyset, adodbLockOptimistic

    Do Until rsLocal.EOF
        ' 外部DBのレコードを特定して更新
        ' ※実務ではIDによる検索を効率的に行う設計が必要
        adoRs.FindMax "SaleID = " & rsLocal!SaleID ' 簡易的な実装例
        
        ' 本来はSQLのUPDATE文を発行するか、ADOの更新機能を使う
        ' ここでは概念を示すために、ADOのRecordset更新の流れを記述
        ' (実際にはSQL文を組み立てて adoConn.Execute する方が高速な場合が多い)
        
        ' ... 更新処理 ...
        
        rsLocal.MoveNext
    Loop
    
    ' すべて成功したらコミット
    adoConn.CommitTrans
    Debug.Print "Process Completed Successfully!"

ExitSub:
    ' オブジェクトの解放(メモリリーク防止の鉄則!)
    On Error Resume Next
    If Not rsLocal Is Nothing Then rsLocal.Close: Set rsLocal = Nothing
    If Not adoRs Is Nothing Then adoRs.Close: Set adoRs = Nothing
    If Not adoConn Is Nothing Then adoConn.Close: Set adoConn = Nothing
    Set db = Nothing
    Exit Sub

ErrorHandler:
    ' エラー時はロールバックして安全に終了
    If Not adoConn Is Nothing Then
        If adoConn.State = adStateOpen Then adoConn.RollbackTrans
    End If
    MsgBox "Error: " & Err.Description, vbCritical
    Resume ExitSub
End Sub

4. 陥りやすい「3つの罠」と回避策

この高度な手法を使う際、プロでも陥るミスがあります。ここをクリアすれば、あなたはもう初心者ではありません。

① オブジェクトの「解放忘れ」によるメモリリーク

VBAでは、`Set rs = Nothing` を忘れると、プログラムが終わってもメモリやデータベースの接続が掴みっぱなしになることがあります。特に、ループ内でRecordsetを生成する場合は要注意です。
対策: `Exit Sub` の前に必ず全てのオブジェクトを `Close` し、`Nothing` を代入する習慣をつけましょう。

② トランザクションの範囲(Scope)の誤解

「途中でエラーが起きたのに、一部のデータだけ書き換わってしまった!」というのは、トランザクション管理ができていない証拠です。
対策: 外部DBへの書き戻し(Step 4)は、必ず `BeginTrans` と `CommitTrans` で囲んでください。エラーが起きたら `RollbackTrans` で「なかったこと」にする。これがデータの整合性を守るプロの仕事です。

③ 「1件ずつ更新」の誘惑

ADOを使って、ループの中で毎回 `UPDATE` 文を `Execute` するのは、ネットワーク越しでは非常に低速です。
対策: 大量にある場合は、一時テーブルをCSVとして出力してサーバー側のインポート機能を使うか、ADOのRecordsetをバッチ更新する設計を検討してください。

最後に:エンジニアとしての視点

ここまで読んでくれたあなたなら、もうお分かりのはずです。
プログラミングの真髄は、「コードを綺麗に書くこと」だけではありません。「コンピュータの動き(データがどこからどこへ動くのか)」を予測し、最も負荷の低いルートを設計することにあります。

DAOとADOのハイブリッド設計は、そのための強力な武器になります。最初は難しく感じるかもしれませんが、一度この「アーキテクチャの視点」を手に入れれば、どんなに重い業務システムも、あなたの手によって軽やかに動かせるようになるはずですよ。

さあ、自信を持って、次のコードを書き始めてください!応援しています。

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