Accessを「単なる道具」から「高精度データエンジン」へ昇華させる:大規模テーブル正規化の極意
Accessを扱っていると、必ず遭遇する「負の遺産」がある。数百万レコードを抱え、フラットに積み上げられた巨大なテーブル。クエリは重く、レコードの更新はロックの嵐。これを手動で正規化するなど、エンジニアのすることではない。
今日は、DAO(Data Access Objects)を極限まで使い倒し、システムを止めることなく、あるいは最小限のダウンタイムで「フラットな混沌」を「秩序あるリレーショナル構造」へ変換する、自動化アーキテクチャの真髄を伝授する。
—
1. 破壊的リファクタリングの哲学
大規模なテーブル分割には、ただデータを移すだけのスクリプトでは不十分だ。以下の3点を遵守せよ。
- 完全なトランザクション管理: 失敗は許されない。`BeginTrans`と`CommitTrans`を使い、データの整合性を物理的に担保する。
- オブジェクトのライフサイクル管理: `Set obj = Nothing`は当然だが、それ以上に「コレクションを走査する際のポインタの挙動」を理解せよ。
- メタデータの操作: `TableDef`と`Relation`オブジェクトを動的に生成・削除する手法こそが、レガシーを近代化する鍵だ。
—
2. 実装:正規化自動化エンジンの心臓部
以下は、フラットテーブルから「親テーブル」と「子テーブル」を切り出し、リレーションを自動再構築するプロトタイプだ。
Option Compare Database
Option Explicit
‘ 大規模データ移行の核心:DAOを用いたテーブル再構築
Public Sub ExecuteDataNormalization()
Dim db As DAO.Database
Dim td As DAO.TableDef
Dim rsSource As DAO.Recordset
Dim rsParent As DAO.Recordset
Dim rsChild As DAO.Recordset
Set db = CurrentDb
‘ トランザクション開始:整合性が守れないなら全ロールバック
DBEngine.BeginTrans
On Error GoTo ErrorHandler
‘ 1. スキーマ定義(省略:事前にCreateTableDefで親・子テーブルを生成しておくこと)
‘ 2. データの転送と正規化
Set rsSource = db.OpenRecordset(“SELECT FROM tbl_Flat”, dbOpenSnapshot)
Set rsParent = db.OpenRecordset(“tbl_Parent”, dbOpenDynaset)
Set rsChild = db.OpenRecordset(“tbl_Child”, dbOpenDynaset)
Do Until rsSource.EOF
‘ 親テーブルへの挿入
rsParent.AddNew
rsParent!ParentName = rsSource!ParentName
rsParent.Update
‘ 子テーブルへの挿入(リレーションキーを伝播させる)
rsChild.AddNew
rsChild!ParentID = rsParent.LastModified ‘ 直前の挿入IDを取得
rsChild!ChildData = rsSource!ChildData
rsChild.Update
rsSource.MoveNext
Loop
‘ 3. リレーションシップの動的構築
CreateRelationship db, “rel_Parent_Child”, “tbl_Parent”, “tbl_Child”, “ID”, “ParentID”
DBEngine.CommitTrans
MsgBox “正規化完了。メモリ解放へ移行します。”, vbInformation
Cleanup:
‘ オブジェクトを明示的に解放し、メモリリークを根絶する
If Not rsSource Is Nothing Then rsSource.Close: Set rsSource = Nothing
If Not rsParent Is Nothing Then rsParent.Close: Set rsParent = Nothing
If Not rsChild Is Nothing Then rsChild.Close: Set rsChild = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
DBEngine.Rollback
Debug.Print “Error: ” & Err.Description
Resume Cleanup
End Sub
‘ リレーションシップを動的に生成する関数
Private Sub CreateRelationship(db As DAO.Database, relName As String, _
parentTable As String, childTable As String, _
parentField As String, childField As String)
Dim rel As DAO.Relation
Dim fld As DAO.Field
Set rel = db.CreateRelation(relName, parentTable, childTable, dbRelationUpdateCascade)
Set fld = rel.CreateField(parentField)
fld.ForeignName = childField
rel.Fields.Append fld
db.Relations.Append rel
End Sub
—
3. シニアエンジニアが意識すべき「隠れたコスト」
メモリとパフォーマンスの境界線
DAOの`dbOpenDynaset`は強力だが、レコードセットが巨大になるとメモリを圧迫する。数百万件を超える場合は、`dbOpenForwardOnly`(前進のみ)を使用せよ。これは速度を劇的に向上させる。
Windows APIによるUIフリーズの回避
長時間処理を行う際、Accessの画面が「応答なし」になるのを防ぐため、`DoEvents`をループ内に適切に配置する。しかし、多用は禁物だ。`GetTickCount` APIを併用し、ミリ秒単位で制御することで、オーバーヘッドを最小限に抑えるのが真のプロフェッショナルである。
レガシー保守への提言
既存システムを破壊せず正規化するためには、「新旧共存期間」を設けることだ。旧テーブルを`tbl_Flat_Archive`にリネームし、ビュー(クエリ)で旧来の参照先を新しい正規化テーブルへ向ける。これにより、フロントエンドの改修を最小限に留めつつ、バックエンドの品質を劇的に向上させることが可能になる。
—
最後に
Accessは決して「おもちゃ」ではない。それをどう使いこなすかは、設計者の知性とDAO/APIの深い理解にかかっている。フラットなテーブルを正規化する作業は、単なるデータ移設ではなく、システムに新しい命を吹き込む外科手術である。
恐れずにコードを書け。ただし、細部まで神経を通わせることを忘れるな。それが、システムを10年先まで現役で動かし続けるための唯一の道だ。
