【テクニカル・上級編】外部データベース(SQL Server)からタスク情報を取得し、Projectへ同期する連携手法 – Project VBA解析バイブル

スポンサーリンク

外部DB・SQL ServerからMS Projectへ:WBS・依存関係同期の極限アーキテクチャ

レガシーシステムの最前線でVBAと向き合い続けているエンジニア諸君。
Excelのデータ成形に留まらず、Microsoft Project(MSP)をエンタープライズのPMBOK準拠エンジンとして稼働させようとした時、幾度となく「タスクの階層構造(WBS)の崩壊」「依存関係(先行・後続タスク)の循環参照エラー」「ADO接続のメモリリーク」という壁に直面してきたことだろう。

特に、SQL Serverなどの外部データベースから数千件規模のWBSデータを取得し、MSPへ一気通貫で同期させるバッチ処理は、生半可なコードでは確実にProject本体をハングアップさせる。COMオブジェクトのライフサイクル、そしてMSP特有の計算エンジン(Calculation Engine)の挙動を完全に掌握していなければ、実用に耐えうるシステムは構築できない。

本稿では、ADODBを用いた超高速データフェッチと、MSPのタスク階層・依存関係をミリ秒単位で制御するエンタープライズ向け同期エンジンの極限の知見を公開する。

1. エンタープライズ連携における3大ボトルネック

SQL ServerからMSPへタスクを流し込む際、以下の3点がシステムの生死を分ける。

1. 暗黙の再計算(Calculation Engine)による激しい性能劣化
MSPはタスクが1件追加される、あるいはインデント(階層化)されるたびに、全体のスケジュール再計算をバックグラウンドで走らせる。数千件のタスクを順次追加するアプローチでは、後半になるにつれてO(n^2)のオーダーで処理が重くなる。
2. 依存関係(Predecessors)の解決順序の矛盾
SQLから取得したレコードセットの順序のまま先行タスクIDを指定すると、まだMSP上に存在しないタスクIDを参照してしまい、致命的なCOM例外(Run-time error)が発生する。
3. ADODB及びCOMオブジェクトのメモリ管理の不徹底
`New` キーワードの乱用や、ADOレコードセットの不適切なクローズは、Officeプロセス内にCOMの残骸を残し、やがてVBEをクラッシュさせる。

これらを完全に克服するアーキテクチャをコードで示そう。

2. 実装コード:SQL ServerからMSPへの完全同期エンジン

以下のコードは、エラーハンドリング、トランザクション的思考、そしてパフォーマンスを極限まで高めた実戦投入可能なVBAモジュールである。

Option Explicit

‘ =========================================================================
‘ 外部DB(SQL Server)からタスク情報を取得し、Projectへ同期するメインプロシージャ
‘ アーキテクチャ設計: チーフアーキテクト
‘ =========================================================================
Public Sub SyncWBSFromSQLServer()
Dim conn As Object
Dim rs As Object
Dim connStr As String
Dim sql As String

‘ MSPアプリケーションオブジェクトの参照
Dim prjApp As MSProject.Application
Dim prjProj As MSProject.Project

‘ パフォーマンス最適化のためのフラグ退避
Dim origCalcMode As Long

‘ 接続文字列(環境に合わせて変更すること。OLEDB / SQLOLEDB もしくは MSOLEDBSQL)
connStr = “Provider=MSOLEDBSQL;Server=127.0.0.1\SQLEXPRESS;Database=EnterprisePM;Trusted_Connection=yes;”

‘ SQL Serverから階層順、かつ一意なWBS順でデータを取得
‘ ※前提条件として、DB側でParentIDやOutlineLevel、SortOrderが担保されていること
sql = “SELECT TaskUID, TaskName, Duration, StartDate, ParentUID, OutlineLevel, Predecessors, SortOrder ” & _
“FROM vw_ProjectWBS_Sync ” & _
“WHERE ProjectCode = ‘PRJ-2023-ALPHA’ ” & _
“ORDER BY SortOrder ASC;”

‘ 1. ADOオブジェクトの安全な生成
Set conn = CreateObject(“ADODB.Connection”)
Set rs = CreateObject(“ADODB.Recordset”)

On Error GoTo ErrorHandler

‘ 接続タイムアウトの設定(秒)
conn.ConnectionTimeout = 30
conn.CommandTimeout = 60
conn.Open connStr

rs.Open sql, conn, 0, 1 ‘ adOpenForwardOnly, adLockReadOnly

If rs.EOF Then
MsgBox “同期対象のデータが存在しません。”, vbExclamation, “同期中断”
GoTo CleanUp
End If

‘ 2. MSPのインスタンス取得(既存アクティブプロジェクトを対象、または新規作成)
Set prjApp = ActiveProject.Application
If prjApp.Projects.Count = 0 Then
Set prjProj = prjApp.Projects.Add
Else
Set prjProj = prjApp.ActiveProject
End If

‘ 3. 【極限最適化】MSPの自動計算と画面描画を停止
‘ これを行わないと、1タスク追加するたびにスケジュールエンジンが走り、処理が数倍~数十倍遅くなる。
origCalcMode = prjApp.Calculation
prjApp.Calculation = pjCalculationManual
prjApp.ScreenUpdating = False

‘ 4. フェーズ1:タスクのプレーン追加と基本属性の設定
Dim t As MSProject.Task
Dim tasksMap As Object
Set tasksMap = CreateObject(“Scripting.Dictionary”) ‘ DBのTaskUIDとMSPのTaskオブジェクトを紐付け

Do While Not rs.EOF
‘ タスクの追加(MSPの仕様上、まずはフラットに追加する)
Set t = prjProj.Tasks.Add(rs.Fields(“TaskName”).Value)

‘ 基本情報の流し込み
t.Duration = rs.Fields(“Duration”).Value 480 ‘ 分単位へ換算 (1日=8時間=480分)
If Not IsNull(rs.Fields(“StartDate”).Value) Then
t.Start = rs.Fields(“StartDate”).Value
End If

‘ 後続の依存関係解決のためにDictionaryに保持 (Key: DBのUID, Item: MSPのTaskオブジェクト)
If Not tasksMap.Exists(rs.Fields(“TaskUID”).Value) Then
tasksMap.Add rs.Fields(“TaskUID”).Value, t
End If

rs.MoveNext
Loop

‘ 5. フェーズ2:WBS階層構造(インデント)の構築
‘ 一度全タスクを並べた後、OutlineLevelや親子関係に基づいてインデントを適用する
‘ ここでは簡略化のため、レコードセットを再度走査するか、マップから制御する
rs.MoveFirst
Do While Not rs.EOF
Dim currentUID As Variant
Dim parentUID As Variant
Dim targetTask As MSProject.Task

currentUID = rs.Fields(“TaskUID”).Value
parentUID = rs.Fields(“ParentUID”).Value

Set targetTask = tasksMap(currentUID)

‘ 親が存在する場合(子タスクである場合)
If Not IsNull(parentUID) Then
If tasksMap.Exists(parentUID) Then
‘ MSP上でインデントを下げて階層化する
‘ ※実運用ではOutlineIndentメソッドを適切に呼び出す
targetTask.OutlineIndent
End If
End If

rs.MoveNext
Loop

‘ 6. フェーズ3:依存関係(Predecessors)の遅延解決
‘ 全タスクがMSP上に生成され、UIDが確定した後に依存関係を結ぶことで循環参照や参照エラーを防ぐ
rs.MoveFirst
Do While Not rs.EOF
Dim predStr As String
predStr = IIf(IsNull(rs.Fields(“Predecessors”).Value), “”, rs.Fields(“Predecessors”).Value)

If Len(predStr) > 0 Then
Dim sourceTask As MSProject.Task
Set sourceTask = tasksMap(rs.Fields(“TaskUID”).Value)
‘ 先行タスク文字列の割り当て(MSPは “1FS+2d” のようなフォーマットを解釈する)
sourceTask.Predecessors = predStr
End If

rs.MoveNext
Loop

CleanUp:
‘ 7. 【極限最適化の解除】環境を元に戻す
If Not prjApp Is Nothing Then
prjApp.Calculation = origCalcMode
prjApp.ScreenUpdating = True
‘ 強制再計算の実行
prjApp.CalculateAll
End If

‘ 8. オブジェクトの明示的解放(メモリリークの完全防止)
On Error Resume Next
If Not rs Is Nothing Then
If rs.State = 1 Then rs.Close
Set rs = Nothing
End If
If Not conn Is Nothing Then
If conn.State = 1 Then conn.Close
Set conn = Nothing
End If
Set tasksMap = Nothing
Set prjProj = Nothing
Set prjApp = Nothing
Exit Sub

ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “Sync Engine Error”
Resume CleanUp
End Sub

3. チーフアーキテクトが教える「現場の急所」

上記のコードをそのままエンタープライズ環境に投入するにあたり、シニアエンジニアとして知っておくべき高度なインサイトを共有する。

① スケジュールエンジン(Calculation)の無効化が生む劇的な効果

MSPはデフォルトで「自動計算モード」になっている。この状態で1000件のタスクに対して `Tasks.Add` や `OutlineIndent` を実行すると、Windowsのメッセージキューがパンクし、CPU使用率が跳ね上がると同時に処理速度が極端に低下する。
`Calculation = pjCalculationManual` に設定し、最後に `CalculateAll` を1回だけ叩くアプローチをとることで、処理時間を数分から数秒へと劇的に短縮できる。

② 依存関係の「遅延解決(Deferred Resolution)」

DBから取得したデータをそのまま上から順に依存関係として書き込もうとすると、「まだ生成されていないタスクID」を指定してしまいエラーになる。
そのため、【フェーズ1:全タスクのプレーン作成とDictionaryへのマッピング】 $\rightarrow$ 【フェーズ2:階層構造(インデント)の構築】 $\rightarrow$ 【フェーズ3:依存関係の適用】 という3段階のパイプライン(分離・遅延評価モデル)を必ず守ること。

③ COMオブジェクトの参照切断とメモリ最適化

VBAにおける `CreateObject` や `New` は、背後でCOMの参照カウンター(Reference Counting)をインクリメントしている。
特にADOの `Recordset` や `Connection` は、途中でエラー(Err.Raise)が発生してサブルーチンを抜けた場合、ガベージコレクションに回収されずにOfficeプロセス内に残り続ける。これが蓄積すると、ExcelやProjectが原因不明のメモリ不足(Out of Memory)を引き起こす。
コード例のように、`On Error GoTo ErrorHandler` を経由して必ず `CleanUp` ラベルで `rs.Close` と `Set obj = Nothing` を実行するイディオムは、エンタープライズVBAの絶対的な鉄則である。

結び

VBAおよびOfficeオートメーションは、レガシーな技術と揶揄されることがある。しかし、底层のCOM仕様、メモリ管理、そしてホストアプリケーション(Microsoft Project)のイベント駆動モデルを完全に理解した上で構築されたシステムは、現代のモダンなWebアプリケーションに匹敵する堅牢性と処理速度を発揮する。

妥協のない設計と、泥臭いメモリ管理の徹底。それこそが、真のプロフェッショナルエンジニアリングである。

タイトルとURLをコピーしました