MS Project×SQL Server:コスト予実管理を「自動化」ではなく「掌握」するアーキテクチャ
多くの開発者が、MS Projectのコスト管理で泥沼にはまる理由は一つだ。「手動更新の罠」と「オブジェクトモデルへの理解不足」である。
現場でよく見る「ボタンを押すと全タスクをループして値を書き込む」というVBAコードは、データ量が増えた瞬間にフリーズし、排他制御の失敗でプロジェクトファイルを破壊する。
本記事では、Project VBAを「単なるスクリプト」ではなく「堅牢なエンタープライズ連携基盤」へと昇華させるための極意を伝授する。
—
1. 陥りがちなアンチパターン
もし君が、`ActiveProject.Tasks`を単純ループで回し、都度`SQL Server`へ接続しに行っているなら、今すぐその実装を捨てろ。
- 非効率なI/O: タスク数×リソース数のループ内でのDB接続は、レイテンシの塊だ。
- 計算の競合: `Cost`フィールドに値を上書きし続けると、Project標準の再計算エンジンと衝突し、意図しない数値の乖離を招く。
- 不適切なフィールド利用: `Cost`フィールドに直接書き込むのは、Projectのスケジューリングロジックを破壊する禁じ手だ。必ず「カスタムコストフィールド(Cost1, Cost2等)」を演算用に使用せよ。
2. 堅牢なデータ連携のための設計思想
SQL Serverからデータを引き抜き、Projectへ流し込む際は以下の「3層構造」を徹底すること。
1. 抽出層 (Extraction): SQLで必要なリソース単価と工数を1つのRecordsetに集約する(バッチ処理の基本)。
2. 変換層 (Mapping): ProjectのタスクIDとリソースIDをキーにした連想配列(Dictionary)を作成する。
3. 反映層 (Application): `Application.ScreenUpdating = False`を徹底し、トランザクションを最小化して一括反映する。
—
3. 実践コード:高速かつ安全なコスト反映ロジック
以下は、SQL Serverから取得したデータを、Projectのカスタムフィールド(Cost1)へ反映するプロダクションコードの骨子だ。
Option Explicit
‘ 参照設定: Microsoft ActiveX Data Objects 6.x Library を追加すること
‘ 参照設定: Microsoft Project 16.0 Object Library
Public Sub UpdateProjectCostsFromSQL()
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim proj As Project
Dim tsk As Task
Dim costDict As Object
Set proj = ActiveProject
Set costDict = CreateObject(“Scripting.Dictionary”)
‘ 1. SQLから一括取得(ネットワーク負荷を最小化)
Set conn = New ADODB.Connection
conn.ConnectionString = “Provider=SQLOLEDB;Data Source=YourServer;Initial Catalog=YourDB;Integrated Security=SSPI;”
conn.Open
‘ リソース単価と実績工数をジョインして取得
Set rs = conn.Execute(“SELECT TaskUID, ExpectedCost FROM View_ProjectCostSync”)
Do While Not rs.EOF
costDict.Add rs!TaskUID, rs!ExpectedCost
rs.MoveNext
Loop
‘ 2. 反映処理(画面更新を止めて高速化)
Application.ScreenUpdating = False
On Error GoTo Cleanup
For Each tsk In proj.Tasks
If Not tsk Is Nothing Then
If costDict.Exists(tsk.UniqueID) Then
‘ Cost1に実績コストを格納(Project標準のCostフィールドは変更しないのが鉄則)
tsk.Cost1 = costDict(tsk.UniqueID)
End If
End If
Next tsk
Cleanup:
Application.ScreenUpdating = True
If Not rs Is Nothing Then rs.Close
If Not conn Is Nothing Then conn.Close
Set rs = Nothing: Set conn = Nothing
MsgBox “コスト更新が完了しました。”, vbInformation
End Sub
—
4. プロの視点:安定稼働のための注意点
オブジェクトのライフサイクル
`ADODB.Connection`や`Recordset`は、スコープを抜ける前に必ず`Close`し、`Nothing`を代入せよ。これを怠ると、Projectのメモリリークを誘発し、長時間稼働時に確実にツールが落ちる。
データの「信頼性」を担保する
SQL Server側で、`TaskUID`と`ProjectID`のペアでインデックスを張っておくこと。ProjectのタスクIDは移動や削除で変わる可能性があるが、`UniqueID`は不変だ。これを利用しない設計は、半年後に必ずデータ不整合を引き起こす。
予実差分の可視化
差分(予実管理)は、Project上のカスタムフィールドで計算式を設定しておくのが最も効率的だ。
- `[Cost1]`(SQLから取得した予算)
- `[Cost]`(Projectが計算した現在コスト)
- `[Number1]`(カスタムフィールド)に `[Cost] – [Cost1]` という計算式を入れれば、VBA側で複雑な演算を行う必要はなくなる。
最後に
自動化とは、ただ楽をすることではない。「人間が手作業で介入する余地(=バグの混入経路)を極限まで排除すること」である。
このコードをベースに、君のプロジェクトの要件に合わせて拡張してほしい。迷った時は「Projectの標準エンジンと戦っていないか?」と自問自答せよ。それが、システムを壊さない唯一の道だ。
