Access VBAの深淵:運用DBを「完全同期」させる極限のDDLエンジン構築術
Access開発の現場で最も不毛な作業は何か? それは、運用中のフロントエンド・バックエンド構成において、開発環境で行った「わずか1カラムの追加」や「インデックスの変更」を、手作業で本番環境に反映させることだ。
多くのジュニアエンジニアは、テーブルを丸ごと入れ替える力技に出る。だが、それは自殺行為だ。データが蓄積された運用DBにおいて、テーブルの再構築は破壊を意味する。
今日は、ADOXとDAOを完璧に使い分け、差分のみを安全かつ確実に適用する「完全同期エンジン」の設計思想と実装コードを伝授する。
—
なぜ「DAO」と「ADOX」の両刀使いが必要なのか
Accessのテーブル定義操作には、DAO(Data Access Objects)とADOX(ADO Extensions for DDL and Security)の二つが存在する。
- DAO: Accessのネイティブオブジェクト。高速で、特にインデックスやリレーションシップの制御においてAccessの仕様を正確に叩ける。
- ADOX: OLE DBプロバイダ経由の汎用的なDDL操作。列の属性変更や、DAOでは少し癖のあるメタデータ操作に長けている。
「片方だけで十分では?」という問いは、現場を知らない者の甘さだ。DAOは高速だが、特定のプロパティ変更で不安定になることがある。逆にADOXは柔軟だが、リレーションシップの緻密な制御には向かない。
真のアーキテクトは、「構造変更はADOX、整合性維持はDAO」という境界線を引き、両者を適材適所で融合させる。
—
堅牢な同期エンジンの設計哲学
同期エンジンを作る際に守るべき「3つの鉄則」がある。
1. 冪等性(Idempotency): 何度実行しても同じ状態になること。既に存在するカラムを追加しようとしてエラーを吐くコードは、ゴミ箱行きだ。
2. アトミックなトランザクション: DDL操作は本来トランザクションの対象外であることが多いが、エラー時は例外処理で確実にログを吐き、ロールバックの代替手段(ログ分析)を用意すること。
3. メタデータ駆動: 「コード内に変更内容を書く」のではなく、「定義用テーブル」や「マスターDB」の情報を読み取り、自動生成する仕組みにすること。
—
実装コード:DDL同期エンジン(抜粋)
このコードは、開発側(`strSourcePath`)の定義を、運用側(`strDestPath`)に反映させるためのコアモジュールだ。
‘ 必要な参照設定: Microsoft ADO Ext. 2.8 for DDL and Security
‘ 必要な参照設定: Microsoft Office 16.0 Access database engine Object Library
Public Sub SynchronizeTableSchema(strSourcePath As String, strDestPath As String, strTableName As String)
Dim catSource As New ADOX.Catalog
Dim catDest As New ADOX.Catalog
Dim tblSource As ADOX.Table
Dim tblDest As ADOX.Table
Dim colSource As ADOX.Column
‘ 1. カタログの接続
catSource.ActiveConnection = “Provider=Microsoft.ACE.OLEDB.12.0;Data Source=” & strSourcePath
catDest.ActiveConnection = “Provider=Microsoft.ACE.OLEDB.12.0;Data Source=” & strDestPath
Set tblSource = catSource.Tables(strTableName)
Set tblDest = catDest.Tables(strTableName)
‘ 2. カラムの差分検出と適用
For Each colSource In tblSource.Columns
If Not ColumnExists(tblDest, colSource.Name) Then
‘ カラムが存在しない場合のみ追加
On Error Resume Next
tblDest.Columns.Append colSource.Name, colSource.Type, colSource.DefinedSize
If Err.Number <> 0 Then Debug.Print “Error adding column: ” & colSource.Name
On Error GoTo 0
End If
Next
‘ 3. リレーションシップの同期はDAOで行う(後述)
SyncRelationships strSourcePath, strDestPath, strTableName
Set catSource = Nothing
Set catDest = Nothing
End Sub
Private Function ColumnExists(tbl As ADOX.Table, colName As String) As Boolean
Dim col As ADOX.Column
For Each col In tbl.Columns
If col.Name = colName Then ColumnExists = True: Exit Function
Next
ColumnExists = False
End Function
—
プロダクション環境での重要注意点
1. リレーションシップの「連鎖削除」に注意せよ
DAOを使用してリレーションシップを同期する際、`Attributes`プロパティを適切に設定しなければ、参照整合性が破壊される。`dbRelationUpdateCascade`や`dbRelationDeleteCascade`を明示的に指定し、ソース側の定義を正確にコピーすること。
2. インデックスの再構築
インデックスはテーブル定義とは別オブジェクトだ。`ADOX.Index`オブジェクトを使用し、`Unique`や`PrimaryKey`の設定をソースと完全に一致させること。これを怠ると、パフォーマンスが劇的に低下する。
3. 排他制御の壁
運用環境のバックエンドDBは、常に誰かが接続している可能性がある。同期処理を行う際は、必ず排他モード(Exclusive)でのオープンを試みるか、ユーザーに一時的な切断を促すメッセージ制御を組み込むこと。これができないなら、同期エンジンは「夜間自動実行」に限定すべきだ。
—
最後に:なぜ「ツール」を創るのか
単にスクリプトを書いて終わりではない。この同期エンジンを構築することは、「手作業によるミスという変数」をシステムから完全に排除することを意味する。
プロのエンジニアにとって、コードは単なる記述ではない。それは「二度と同じ過ちを繰り返さないための防壁」だ。今日紹介したこのエンジンをベースに、君たちの現場の要件に合わせて拡張してほしい。
もし、さらに深い最適化や、DAO/ADOXのメモリリーク対策といった「極限のチューニング」が必要になったら、またいつでも尋ねてくれ。エンジニアリングの道に終わりはない。
