【上級】SQL ServerからAutoCADへ:BOM自動生成エンジンを極める設計思想と実装
開発現場で最も恐れられる瞬間の一つは、「設計変更に伴うBOM(部品表)の転記ミス」だ。
外部のデータベース(SQL Server)で厳密に管理されている最新の設計データを、手作業あるいは場当たり的なマクロでAutoCADの図面(`AcadTable`)に流し込む――このアプローチは、大規模な案件において必ず破綻する。
私はこれまでに数々のプラント、建築、機械設計の自動化プロジェクトを手に入ってきたが、「データベースと図面の同期」において妥協したシステムは、例外なく手戻りの山を生んできた。
今回は、ADO(ActiveX Data Objects)を駆使してSQL Serverからセキュアかつ高速にデータを引き抜き、AutoCADの`AcadTable`をダイナミックに生成・更新する「プロダクション品質のBOM自動化エンジン」の全貌を伝授しよう。
単に動くだけのコードではない。オブジェクトのライフサイクル、メモリ管理、そしてトランザクションの概念まで踏み込んだ、プロフェッショナルのための実装を解説する。
—
1. なぜ素朴なVBAコードは実務で破綻するのか?
多くのエンジニアが書く初学者のコードは、次のようなアンチパターンに満ちている。
- エラーハンドリングの欠如: DB接続が切れた瞬間、AutoCADがフリーズする。
- COMオブジェクトの解放漏れ: `Recordset`や`Connection`を野ざらしにし、ExcelやAutoCADのプロセスがタスクマネージャーに残留する。
- テーブルの力技削除と再生成: 既存の`AcadTable`のセルを1つずつループで書き換えるか、無条件に削除して再描画するため、図面内のレイアウトや参照関係が崩壊する。
これらを回避し、「何度実行しても安全(冪等性:Idempotency)」かつ「高速」な仕組みを構築するのが、シニアアーキテクトの仕事だ。
—
2. アーキテクチャの全体像
今回構築するBOM自動化エンジンのデータフローは以下の通りだ。
1. 接続確立: ADO (`ADODB.Connection`) を用いてSQL Serverへ安全に接続。
2. データフェッチ: パラメータ化クエリにより、対象図面(あるいはアセンブリ)の最新部品リストを取得。
3. 図面内検索: 図面データベース (`AcadModelSpace` または `AcadPaperSpace`) を走査し、既にBOMテーブルが存在するか確認。
4. 動的生成 or 更新:
- 存在する場合は、行数を動的にリサイズし、データを流し込む(既存の枠組みを維持)。
- 存在しない場合は、新規に`AcadTable`を生成する。
5. リソース解放: 接続とCOMオブジェクトを確実に破棄。
—
3. プロダクションコード:BOM自動更新エンジン
以下のコードは、エラーハンドリング、トランザクション的思考、そしてCOMの適切な解放を網羅した、そのまま現場に投入できるモジュールである。
Option Explicit
‘ —————————————————————–
‘ 外部データベース(SQL Server)連携BOM自動生成エンジン
‘ 前提条件: 参照設定に “Microsoft ActiveX Data Objects x.x Library” を追加すること
‘ —————————————————————–
Public Sub GenerateOrUpdateBOM()
Dim conn As Object
Dim rs As Object
Dim connStr As String
Dim sql As String
‘ 1. 接続文字列の定義 (環境に合わせて書き換えてください)
‘ ※プロダクション環境では、接続情報はレジストリや設定ファイル、環境変数から取得すべきです
connStr = “Provider=MSOLEDBSQL;Server=YOUR_SERVER_NAME;Database=YOUR_DB_NAME;” & _
“Trusted_Connection=yes;Integrated Security=SSPI;”
‘ 2. SQLクエリの定義 (例: アセンブリIDに紐付く部品リストを取得)
sql = “SELECT PartNo, PartName, Material, Quantity, Remarks ” & _
“FROM dbo.BOM_Master ” & _
“WHERE DrawingNo = ‘DWG-2023-001’ ” & _
“ORDER BY SortOrder ASC;”
On Error GoTo ErrorHandler
‘ ADOオブジェクトのインスタンス化
Set conn = CreateObject(“ADODB.Connection”)
Set rs = CreateObject(“ADODB.Recordset”)
‘ DB接続のオープン
conn.ConnectionTimeout = 15
conn.Open connStr
‘ レコードセットの取得
Set rs = conn.Execute(sql)
If rs.EOF And rs.BOF Then
MsgBox “対象のBOMデータがSQL Server上に見つかりませんでした。”, vbExclamation, “BOM自動化”
GoTo CleanUp
End If
‘ AutoCAD側処理の実行
Call SyncAcadTable(rs)
MsgBox “BOMの同期が正常に完了しました。”, vbInformation, “BOM自動化”
CleanUp:
‘ 確実なリソース解放(メモリリークの防止)
On Error Resume Next
If Not rs Is Nothing Then
If rs.State Then rs.Close
Set rs = Nothing
End If
If Not conn Is Nothing Then
If conn.State Then conn.Close
Set conn = Nothing
End If
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “致命的エラー”
Resume CleanUp
End Sub
‘ —————————————————————–
‘ AutoCAD図面内のAcadTableを操作・同期するコアプロシージャ
‘ —————————————————————–
Private Sub SyncAcadTable(ByRef rs As Object)
Dim acadDoc As AcadDocument
Dim mSpace As AcadModelSpace
Dim targetTable As AcadTable
Dim ent As AcadEntity
Dim foundTable As Boolean
Dim rowCount As Long
Dim colCount As Long
Dim r As Long, c As Long
Set acadDoc = ThisDrawing
Set mSpace = acadDoc.ModelSpace
foundTable = False
‘ 図面内から既存のBOMテーブルを検索 (カスタムDxfNameやタグで識別するのが実務的)
For Each ent In mSpace
If TypeOf ent Is AcadTable Then
‘ ここでは仮にテーブルのタイトルや特定条件で判定
‘ 実務では Dictionary や ExtData (拡張データ) を付与して識別することを強く推奨
Set targetTable = ent
foundTable = True
Exit For
End If
Next ent
‘ レコードカウントの取得 (Recordsetを配列に展開する前準備)
‘ 飛躍的なパフォーマンス向上のため、GetRowsを使う手もあるが、今回は可読性を重視
rs.MoveLast
rowCount = rs.RecordCount + 1 ‘ ヘッダー行分を加算
rs.MoveFirst
colCount = rs.Fields.Count
If Not foundTable Then
‘ — 新規作成モード —
Dim insertPoint(0 To 2) As Double
insertPoint(0) = 100.0: insertPoint(1) = 100.0: insertPoint(2) = 0.0
‘ 行数、列数、行の高さ、列の幅を指定してテーブルを作成
Set targetTable = mSpace.AddTable(insertPoint, rowCount + 1, colCount, 10.0, 50.0)
targetTable.SetText 0, 0, “部品表 (BOM)”
Else
‘ — 更新モード (既存テーブルのリサイズ) —
‘ 既存テーブルの行数が足りない、あるいは多すぎる場合の動的調整
Dim currentRows As Long
currentRows = targetTable.Rows
If currentRows < (rowCount + 1) Then
targetTable.InsertRows currentRows, (rowCount + 1) - currentRows, 10.0
ElseIf currentRows > (rowCount + 1) Then
Dim i As Long
For i = currentRows – 1 To rowCount + 1 Step -1
targetTable.DeleteRows i, 1
Next i
End If
End If
‘ — ヘッダー行の設定 —
For c = 0 To colCount – 1
targetTable.SetText 1, c, rs.Fields(c).Name
targetTable.SetCellBackgroundColor 1, c, RGB(220, 230, 242) ‘ ヘッダーの色付け
Next c
‘ — データ行の流し込み —
r = 2 ‘ データ開始行は2行目(0:タイトル, 1:ヘッダー)
Do While Not rs.EOF
For c = 0 To colCount – 1
Dim cellValue As String
If IsNull(rs.Fields(c).Value) Then
cellValue = “”
Else
cellValue = CStr(rs.Fields(c).Value)
End If
targetTable.SetText r, c, cellValue
Next c
rs.MoveNext
r = r + 1
Loop
‘ 図面ビューの更新
acadDoc.Regen acActiveViewport
End Sub
—
4. 実務で差が出る「設計の急所」
上記のコードを現場の運用に耐えうるものにするために、シニアとしていくつかの重要なポイントを補足しておこう。
① データベース接続のセキュキュリティ
サンプルコードでは簡略化のためにWindows認証 (`Trusted_Connection=yes`) を用いている。実務において、パスワードをVBAコード内にハードコーディングすることはセキュリティインデシットの観点から絶対に避けるべきだ。接続文字列は安全な外部ファイル(暗号化されたiniやXML、またはWindowsの資格情報マネージャー)から動的にロードするアーキテクチャにすべきである。
② テーブルの特定方法(拡張データの活用)
コード内では `TypeOf ent Is AcadTable` で最初に見つかったテーブルを対象にしているが、図面に複数のテーブル(例えば「改定履歴テーブル」と「部品表テーブル」)が存在する場合、これでは誤動作する。
プロダクションコードでは、テーブルオブジェクトに対して XData(拡張データ) または Dictionary を付与し、「`BOM_TABLE_ID`」というメタデータを持つエンティティのみを厳密にターゲットとして取得する設計にしなければならない。
③ パフォーマンスチューニング(バルク処理)
数千行に及ぶ巨大なBOMを扱う場合、セルを1つずつ `SetText` するアプローチはAutoCADのグラフィックエンジンに負荷をかけ、処理が数秒〜数十秒で終わらなくなることがある。
極限のパフォーマンスを求める場合は、画面描画を一時的に停止する `acadDoc.Utility.Prompt` の活用や、トランザクションを明示的にグループ化する手法(`AcadDatabase.ObjectId` を駆使した低レベル操作)を検討してほしい。
—
5. おわりに
AutoCAD VBAは、単なる「お絵描きを楽にするためのオモチャ」ではない。
正しくオブジェクトモデルを理解し、外部システム(SQL Server)とのパイプラインを堅牢に構築すれば、「設計データの信頼性を担保するミッションクリティカルなシステム」へと昇華させることができる。
今回紹介した設計思想とコードをベースに、あなたの開発現場から「手作業による転記ミス」を完全に根絶してほしい。エンジニアとしての真価が試されるのは、まさにこういう細部へのこだわりなのだから。
