【極限の知見】Accessリレーションシップの迷宮を解き明かす:依存関係グラフ化エンジンの設計思想
現場で長く運用されているAccessデータベースを継承したとき、最も頭を抱えるのが「このテーブルを削除・変更すると、どこが死ぬのか?」という問いだ。リレーションシップウィンドウのスパゲッティ状態を見て溜息をつくのは、もう今日で終わりにしよう。
今日は、Accessのメタデータである`DAO.Relation`を徹底的に掌握し、テーブル間の依存関係を再帰的にトレースして「影響範囲」を可視化するツールを構築する。単なるコードの羅列ではない。実務で「壊れない」と確信できるアーキテクチャを授ける。
—
1. なぜ「力技の解析」は失敗するのか
多くのエンジニアが陥る罠は、`Relation`オブジェクトを単にループさせるだけの設計だ。しかし、これでは「多段依存(A→B→C)」の全貌が見えない。
- 循環参照の放置: テーブル同士がループしている場合、再帰処理でスタックオーバーフローを引き起こす。
- メタデータの断片化: `Relation`オブジェクトは、インデックスが未設定の場合や、定義が壊れている場合に意図しない挙動をする。
- パフォーマンスの欠如: 毎回`CurrentDb`を叩き続けるのは非効率だ。一度メモリ(Dictionary等)にロードし、グラフ構造として保持するのがプロの作法だ。
—
2. 設計指針:依存グラフの構築
今回は、以下の手順で堅牢なツールを作成する。
1. メタデータの収集: `CurrentDb.Relations`を走査し、親テーブルと子テーブルのペアを抽出。
2. グラフ構造の保持: `Scripting.Dictionary`を用いて、親テーブルをキー、子テーブルのリストを値として保持する「隣接リスト」を作成。
3. 再帰的探索: 特定のテーブルから出発し、末端までの依存を深さ優先探索(DFS)で出力する。
—
3. 実装コード:Dependency Graph Engine
以下のコードを標準モジュールに貼り付けよ。`Microsoft Scripting Runtime`を参照設定に追加することを忘れないでくれ。
Option Explicit
‘ 依存関係を保持する辞書
Private m_Graph As Object
Public Sub AnalyzeDependencies(ByVal startTable As String)
Set m_Graph = CreateObject(“Scripting.Dictionary”)
BuildGraph
Debug.Print “— 依存関係ツリー: ” & startTable & ” —”
PrintTree startTable, 0, “”
End Sub
‘ グラフの構築(一度だけ実行する)
Private Sub BuildGraph()
Dim rel As DAO.Relation
Dim childList As Object
For Each rel In CurrentDb.Relations
‘ システムテーブルや隠しテーブルを除外するフィルタリングを入れること
If Left(rel.Table, 4) <> “MSys” Then
If Not m_Graph.Exists(rel.Table) Then
Set childList = CreateObject(“System.Collections.ArrayList”)
m_Graph.Add rel.Table, childList
End If
m_Graph(rel.Table).Add rel.ForeignTable
End If
Next rel
End Sub
‘ 再帰的な可視化(DFS)
Private Sub PrintTree(ByVal tableName As String, ByVal depth As Integer, ByVal visited As String)
Dim indent As String
indent = String(depth 2, ” “)
‘ 循環参照防止のチェック
If InStr(visited, “|” & tableName & “|”) > 0 Then
Debug.Print indent & “-> [” & tableName & “] (循環参照検知)”
Exit Sub
End If
Debug.Print indent & “+- ” & tableName
If m_Graph.Exists(tableName) Then
Dim child As Variant
For Each child In m_Graph(tableName)
PrintTree CStr(child), depth + 1, visited & “|” & tableName & “|”
Next child
End If
End Sub
—
4. アーキテクトからの助言:実務への適用
このコードをただ動かすだけでなく、以下の「現場の知見」を付け加えることで、ツールは真の業務武器となる。
① リレーションシップの「幽霊」を排除せよ
Accessの`Relation`は、テーブルを削除しても稀に整合性が取れなくなることがある。`BuildGraph`の中で、`CurrentDb.TableDefs`に実際にそのテーブルが存在するかを確認するバリデーションを必ず入れること。
② 出力先の柔軟性
`Debug.Print`は開発者用だ。業務担当者に渡すなら、この結果を`TempVars`に格納するか、あるいは一時テーブルを作成して`Accessのフォーム`や`Excel`にエクスポートできるように拡張すべきだ。
③ パフォーマンスの重み
大規模なDB(テーブル数100超)の場合、逐次処理はボトルネックになる。今回あえて`ArrayList`や`Dictionary`を用いたのは、探索コストを最小化するためだ。巨大なDBを解析する際は、一度の解析結果をキャッシュし、UI側で再利用する設計を徹底してほしい。
—
結び
Access VBAは「古い」のではない。適切に設計されたメタデータ処理を行えば、現代のどのフレームワークよりも高速に、データベースの深淵を可視化できる強力なツールだ。
「なぜ動くのか」を理解した者が、現場の混乱を鎮める。このコードをベースに、君自身のデータベースの設計図を塗り替えてみてほしい。健闘を祈る。
