こんにちは! Access VBAの世界へようこそ。
今日は、マクロの記録ボタンを押すだけの世界から一歩抜け出し、プロの現場で使われている「メタデータ駆動型開発」の極意をあなたにお伝えします。
「Excelで作ったテーブル定義書を読み込ませたら、ボタン一つでAccessのテーブルやリレーションが自動で出来上がっちゃう…そんな夢のような仕組みを作りたい!」
今回は、そんなロマン溢れる自動生成エンジンを一緒に作っていきます。「難しそう…」なんて身構えなくて大丈夫。基礎から本質まで、優しく丁寧に紐解いていきますね。ここをクリアすれば、あなたのAccess VBAスキルは間違いなく一段上のステージに到達しますよ!
—
1. なぜ「メタデータ駆動型開発」なのか?
システム開発の現場で一番面倒な作業は何だと思いますか? そう、「テーブル設計変更のたびに、Accessを開いてポチポチとテーブルを手動修正する作業」です。
フィールド名を変え忘れたり、データ型を間違えたり……。人間が手作業で行う以上、ミスはつきものです。そこで登場するのがメタデータ駆動型開発(Metadata-Driven Development)という考え方。
- データ(実体) ではなく、それを記述した メタデータ(データについてのデータ=今回の場合はExcelの定義書) を正としてシステムを動かす。
このアプローチを取ると、Excelの定義書を直してプログラムを実行するだけで、データベースが自動的に最新の状態に同期されるようになります。変更履歴もExcelでバッチリ残せますよね。最高だと思いませんか?
—
2. 準備:Excel定義書の構造を決めよう
まずは、読み込ませるExcel定義書の設計をしましょう。今回はシンプルかつ実用的な構成にします。「TableDefinitions.xlsx」という名前で、シート名は「Fields」とします。
「Fields」シートのレイアウト
| 列A (TableName) | 列B (FieldName) | 列C (DataType) | 列D (Size) | 列E (IsPrimaryKey) |
| :— | :— | :— | :— | :— |
| T_社員マスタ | 社員ID | Text | 10 | True |
| T_社員マスタ | 氏名 | Text | 50 | False |
| T_社員マスタ | 入社日 | Date | 0 | False |
| T_受注明細 | 受注ID | Long | 0 | True |
| T_受注明細 | 社員ID | Text | 10 | False |
- DataType: Text, Long(長整数), Date などを想定します。
- IsPrimaryKey: 主キーにする場合は `True`、それ以外は `False` です。
—
3. 実装:VBA自動生成エンジンの全貌
それでは、いよいよVBAのコードを書いていきましょう。
今回のエンジンは、「DAO(Data Access Objects)」というAccessのデータベース構造を直接いじれる強力な仕組みを使います。
以下のコードを、Accessの標準モジュールにコピー&ペーストしてください。
Option Explicit
‘ =====================================================================
‘ 処理名: Excel定義書からテーブルを自動生成するメインプロシージャ
‘ 概要: 指定されたExcelパスから定義を読み込み、TableDefを構築する
‘ =====================================================================
Public Sub BuildDatabaseFromExcel()
Dim xlApp As Object
Dim xlWb As Object
Dim xlWs As Object
Dim excelPath As String
Dim lastRow As Long
Dim i As Long
Dim currentTable As String
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim db As DAO.Database
‘ 1. 定義書ファイルのパスを指定(同一フォルダ内の前提)
excelPath = CurrentProject.Path & “\TableDefinitions.xlsx”
If Dir(excelPath) = “” Then
MsgBox “テーブル定義書が見つかりません: ” & excelPath, vbCritical, “エラー”
Exit Sub
End If
‘ 2. Excelオブジェクトを起動(バックグラウンド処理)
Set xlApp = CreateObject(“Excel.Application”)
xlApp.Visible = False
Set xlWb = xlApp.Workbooks.Open(excelPath)
Set xlWs = xlWb.Sheets(“Fields”)
‘ 3. データが入力されている最終行を取得
lastRow = xlWs.Cells(xlWs.Rows.Count, “A”).End(xlUp).Row
Set db = CurrentDb()
currentTable = “”
On Error GoTo ErrorHandler
‘ 4. 行を上から順にループ処理
For i = 2 To lastRow
Dim tableName As String
Dim fieldName As String
Dim dataTypeStr As String
Dim fieldSize As Long
Dim isPK As Boolean
‘ Excelから値を変数に取得
tableName = xlWs.Cells(i, 1).Value
fieldName = xlWs.Cells(i, 2).Value
dataTypeStr = xlWs.Cells(i, 3).Value
fieldSize = xlWs.Cells(i, 4).Value
isPK = (LCase(Trim(xlWs.Cells(i, 5).Value)) = “true”)
‘ — テーブルの切り替わりを検知して新規作成 —
If tableName <> currentTable Then
‘ 前のテーブルの主キー設定があればここで確定させるなどの処理も可能
‘ すでに同名テーブルが存在する場合は削除(※本番では慎重に!)
If TableExists(db, tableName) Then
db.TableDefs.Delete tableName
End If
‘ 新しいTableDefオブジェクトを作成
Set tdf = db.CreateTableDef(tableName)
db.TableDefs.Append tdf
‘ 一度追加したテーブルを再取得(フィールド追加のため)
Set tdf = db.TableDefs(tableName)
currentTable = tableName
End If
‘ — フィールドの追加 —
Set fld = tdf.CreateField(fieldName)
‘ データ型の判定と設定
Select Case LCase(dataTypeStr)
Case “text”
fld.Type = dbText
fld.Size = fieldSize
Case “long”
fld.Type = dbLong
Case “date”
fld.Type = dbDate
Case Else
fld.Type = dbText ‘ デフォルト
fld.Size = 255
End Select
‘ フィールドをテーブルに追加
tdf.Fields.Append fld
‘ — 主キー(Primary Key)の設定 —
If isPK Then
Dim idx As DAO.Index
Set idx = tdf.CreateIndex(“PrimaryKey”)
idx.Fields.Append idx.CreateField(fieldName)
idx.Primary = True
tdf.Indexes.Append idx
End If
Next i
‘ 後片付け
xlWb.Close False
xlApp.Quit
Set xlWs = Nothing
Set xlWb = Nothing
Set xlApp = Nothing
MsgBox “テーブルの自動生成が正常に完了しました!”, vbInformation, “成功”
Exit Sub
ErrorHandler:
‘ エラー時のクリーンアップ
If Not xlWb Is Nothing Then xlWb.Close False
If Not xlApp Is Nothing Then xlApp.Quit
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “予期せぬエラー”
End Sub
‘ =====================================================================
‘ 補助関数: 指定したテーブルが既に存在するかチェックする
‘ =====================================================================
Private Function TableExists(db As DAO.Database, tableName As String) As Boolean
Dim tdf As DAO.TableDef
TableExists = False
For Each tdf In db.TableDefs
If tdf.Name = tableName Then
TableExists = True
Exit For
End If
Next tdf
End Function
—
4. コードの深掘り:ここがプロの技術!
上記のコードで、特に重要なポイントをいくつか解説しますね。ここを知っているだけで、VBAの構造理解が何倍も深まります。
① `CreateObject(“Excel.Application”)` による外部操作
AccessからExcelを起動するとき、`New Excel.Application` と書くこともできますが、参照設定のバージョン違いによるエラー(コンパイルエラー)を防ぐために、プロはレイトバインディング(Late Binding)である `CreateObject` を好みます。バックグラウンドでこっそりExcelを立ち上げ、ユーザーに意識させずにデータを吸い上げるのがスマートなエンジニアの流儀です。
② テーブルの「実体化」と「再取得」のタイミング
DAOを使ってテーブルを作る際、`db.CreateTableDef(tableName)` でオブジェクトを作り、最後に `db.TableDefs.Append tdf` でAccessに登録します。
ここで注意なのが、「Appendする前はフィールドを追加できない」というDAO特有のルールです。そのため、一度Appendしたあとに `Set tdf = db.TableDefs(tableName)` ともう一度メモリ上で捉え直して、そこにフィールドを追加していくという手順を踏んでいます。ここ、初心者がよくハマる沼なので覚えておいてくださいね!
③ 主キー(Index)の動的生成
「このフィールドは主キーにしたい」という要望に対し、DAOではインデックスオブジェクトを新しく作り、そこにフィールドを紐づけて `.Primary = True` にした上でテーブルのインデックスコレクションに追加するという手続きを踏みます。少し手間に見えますが、これがプログラムからデータベースの制約を完全にコントロールする唯一の方法です。
—
5. 陥りやすいエラーと対策
プログラミングはエラーとの戦い。この自動生成エンジンを作る際によくあるトラブルをあらかじめシェアしておきます。
- エラー: 「実行時エラー ‘3211’: データベースを排他的にオープンできません…」
- 原因: 自動生成しようとしているテーブルを、現在自分が開いていたり、別のクエリやフォームが掴んでいる状態です。
- 対策: 処理を実行する前には、必ずすべてのテーブルやフォームを閉じましょう。
- エラー: 「実行時エラー ‘3012’: ‘〇〇’ という名前のオブジェクトは既に存在します。」
- 原因: すでに存在するテーブル名に対して、上書き処理をせずに新しく作ろうとしたため。
- 対策: 今回のコードのようにはじめに `TableExists` 関数などで存在チェックを行い、あれば `Delete` する安全策を必ず組み込みましょう。
—
まとめ
お疲れ様でした! 今回は「Excel定義書からテーブルを自動生成するエンジン」という、一歩進んだメタデータ駆動型の世界をご紹介しました。
- Excelという誰もが扱えるインターフェースを仕様書の置き場にする。
- VBAのDAOを駆使して、データベース構造をプログラムから自由自在にコントロールする。
この仕組みをモノにすれば、仕様変更への耐性が圧倒的に高い、プロフェッショナルなデータベースシステムを構築できるようになります。「ここをこう改造すれば、リレーションシップの自動化もできそうだな」なんて、ワクワクしてきませんか?
その好奇心こそが、あなたを最高のエンジニアに育ててくれる最高のエンジンです。ぜひご自身の環境でも試してみてくださいね。バッチリ動いたときは、思わずガッツポーズが出ちゃいますよ!
