計算エンジンの呪縛を解け:MS Project VBAにおける「直接書き込み」による極限の高速化戦略
プロジェクトの規模が数千、数万タスクに膨れ上がったとき、多くの開発者が直面する「壁」がある。それは、MS Projectの計算エンジンによるパフォーマンスの低下だ。
カスタムフィールド(Text1やNumber1など)に計算式(Formula)を埋め込み、それによってプロジェクトの状態を可視化するのは、一見すると「正しい」設計に見える。しかし、大規模なスケジュールの更新、外部データベースとの同期、あるいは複雑な条件分岐を伴う自動化処理において、この「計算式への依存」は致命的なボトルネックへと変貌する。
本記事では、MS Projectの計算エンジンをバイパスし、メモリ上で計算を完結させてから値を直接フィールドへ流し込む、プロフェッショナル向けの「Direct Value Injection(直接値注入)」手法を伝授する。
1. なぜ「計算式(Formula)」はプロの現場で忌避されるのか
まず、なぜ君たちが書いたツールが、現場の巨大なプロジェクトファイルで「重い」と言われるのか、その構造的な理由を理解しなければならない。
計算エンジンのオーバーヘッド
MS Projectの計算エンジンは、タスク間の依存関係(Predecessors/Successors)やリソースの割り当てを考慮した、非常に高度な計算グラフを保持している。カスタムフィールドに数式を設定すると、以下の連鎖が発生する。
1. トリガー: タスクの期間や日付が変更される。
2. 再計算の波及: 依存関係にある全タスクに対して、計算エンジンが走る。
3. 計算式の実行: 各タスクのカスタムフィールドの数式が評価される。
4. 再計算のループ: 数式の計算結果が、さらに別の計算(コスト計算やリソース平準化)に影響を与える場合、計算は再帰的に繰り返される。
タスク数が10,000を超えた環境で、VBAからループを回してプロパティを書き換えるたびにこのプロセスが走ると、計算量は $O(n^2)$ に近い挙動を示し、ツールは実用不可能なレベルまで低速化する。
「ブラックボックス」としての数式
数式は、VBAのデバッグプロセスから隔離されている。数式によって値が変わる場合、エラーが発生した際に「それがデータの異常なのか、数式のロジックミスなのか」の切り分けが困難になる。これは、堅牢な業務システムを構築する上で致命的な欠陥だ。
2. アーキテクチャの転換:計算の主権をVBAへ
プロフェッショナルな設計では、「計算のロジックはVBA(または外部DB)に集約し、MS Projectは単なる『データストア(器)』として扱う」という考え方を取る。
具体的には、以下の3ステップでプロセスを構築する。
1. Decoupling(分離): カスタムフィールドに設定されている既存の数式をクリア(空文字化)する。
2. In-Memory Calculation(メモリ内計算): Projectのエンジンを使わず、VBAのメモリ上で全てのロジックを完結させる。
3. Batch Update(一括書き込み): 計算結果を、計算エンジンを停止させた状態で直接フィールドに書き込む。
3. 実装ガイド:プロダクション・クオリティのコード
以下に、タスクのステータスを複数の属性(完了率、遅延、重要度)から判定し、カスタムフィールド `Text1` に書き込む実用的なコードを示す。
このコードの肝は、`Application.Calculation = pjManual` によるエンジンの停止と、計算式を事前に抹消するプロセスにある。
Option Explicit
''' <summary>
''' プロジェクトのカスタムフィールドを高速に更新するメインルーチン
''' </summary>
Public Sub UpdateCustomFieldsHighPerformance()
Dim proj As Project
Set proj = Application.ActiveProject
' 1. パフォーマンス確保のための初期設定
' 計算モードをマニュアルに切り替え、画面更新を停止する
Application.Calculation = pjManual
Application.ScreenUpdating = False
On Error GoTo ErrorHandler
' 2. 既存の「数式」という呪縛を解く
' 計算式が残っていると、書き込みのたびに再計算が走るため、必ずクリアする
PrepareFields proj
' 3. 高速書き込みフェーズ
' 計算ロジックをタスクごとに分離せず、可能な限りメモリ内で処理する
Dim t As Task
Dim statusValue As String
Debug.Print "Update Start: " & Now
For Each t In proj.Tasks
' 空タスク(サマリータスク等)のハンドリング
If Not t Is Nothing Then
If Not t.IsNull Then
' --- ここにビジネスロジックを集約 ---
' MS Projectの計算エンジンに頼らず、VBAのロジックで判定を行う
statusValue = DetermineTaskStatus(t)
' 直接値を書き込む(Formulaが空なので、極めて高速)
t.Text1 = statusValue
' ----------------------------------
End If
End If
Next t
Debug.Print "Update End: " & Now
CleanUp:
' 4. 状態の復元
' 異常終了時でも必ず計算モードを自動に戻す必要がある
Application.Calculation = pjAutomatic
Application.ScreenUpdating = True
Exit Sub
ErrorHandler:
MsgBox "Critical Error during field update: " & Err.Description, vbCritical
Resume CleanUp
End Sub
''' <summary>
''' カスタムフィールドの数式をクリアする
''' </summary>
Private Sub PrepareFields(ByRef proj As Project)
Dim t As Task
' 全タスクを走査してFormulaを消去(大規模な場合は、特定のフィールドに限定すべき)
' ここではText1を対象とする
For Each t In proj.Tasks
If Not t Is Nothing Then
If Not t.IsNull Then
If t.Formula(pjText) <> "" Then
t.Formula(pjText) = ""
End If
End If
End If
Next t
End Sub
''' <summary>
''' ビジネスロジック:タスクのステータスを判定する
''' </summary>
Private Function DetermineTaskStatus(ByRef t As Task) As String
' 例: 完了率、遅延、重要度に基づく複雑な判定
' Projectの計算式では記述が困難な、多段のIf文や外部参照もここなら自由自在だ
Dim isDelayed As Boolean
Dim isCritical As Boolean
' 遅延判定(現在日と比較)
isDelayed = (t.Finish < Date And t.PercentComplete < 100)
' 重要度判定(ユーザー定義フィールド等を利用)
isCritical = (t.Priority > 500)
' 判定ロジック
If isDelayed And isCritical Then
DetermineTaskStatus = "CRITICAL_DELAY"
ElseIf isDelayed Then
DetermineTaskStatus = "DELAYED"
ElseIf t.PercentComplete = 100 Then
DetermineTaskStatus = "COMPLETED"
Else
DetermineTaskStatus = "ON_TRACK"
End If
End Function
4. 堅牢な設計のためのチーフアーキテクトの助言
この手法を実務に投入する際、君たちが陥りやすい罠と、それを回避するための設計指針を伝える。
① データの「真実の源(Single Source of Truth)」を明確にせよ
カスタムフィールドに値を直接書き込む手法を採用する場合、そのフィールドはもはや「計算結果」ではなく「確定したデータ」となる。
もし、ユーザーが手動でそのフィールドを書き換えた場合、VBAを再実行すると上書きされてしまう。これを防ぐには、「自動更新された値」と「手動入力値」を区別するフラグ用フィールドを別途用意するのが、プロの設計だ。
② データベース連携におけるトランザクション
外部データベース(SQL ServerやAccess等)からデータを取得してカスタムフィールドに反映させる場合、必ず 「DB読み込み → メモリ内計算 → Projectへの一括書き込み」 というフローを守れ。
ループの中で1行ずつSQLを発行し、その都度Projectのフィールドを更新するような設計は、最悪のパフォーマンスを招く。
③ エラーハンドリングの徹底
`Application.Calculation = pjManual` を実行したままエラーで終了すると、ユーザーは「なぜ計算が更新されないんだ!」とパニックに陥る。
`On Error GoTo` を使い、必ず `CleanUp` セクションで `pjAutomatic` に戻す設計を徹底しろ。これは「作法」ではなく「義務」だ。
結論
MS Project VBAにおける真のマスターとは、ツールの機能を使いこなす者だけを指すのではない。ツールの限界(計算エンジンのオーバーヘッド)を知り、その限界を回避するために、自らのコードで制御権を取り戻せる者のことだ。
計算式に頼る「楽な開発」を捨て、メモリ内でロジックを支配する「強い開発」を選択せよ。それが、大規模プロジェクトを支えるエンジニアとしての、唯一の道である。
