プロジェクトを「力技」で動かすな:VBAによる自律型スケジューリングエンジンの極意
プロジェクト管理の現場で、ExcelやProject VBAを「ただのデータ入力ツール」として使っているならば、今すぐその思考を捨てろ。タスクの依存関係(WBS)とリソースの負荷を動的に計算し、自動的にスケジュールを最適化する。これは単なるマクロではなく、「制約充足問題」をVBAのメモリ空間で解く、極めて高度なアルゴリズムの領域だ。
今回は、リソース過負荷を検知し、依存関係(Predecessors)を破壊することなくタスクを後方へシフトさせる「自動負荷平準化エンジン」の設計思想を伝授する。
—
1. 物理的なボトルネックとメモリ管理の禁忌
VBAでの大規模なタスク処理において、最も陥りやすい罠は「オブジェクトの肥大化」と「再計算の無限ループ」だ。
- 明示的解放の義務化: `Task`オブジェクトをループ内で生成・破棄する場合、`Nothing`への代入を怠ればメモリリークは必至だ。VBAのガベージコレクタを信じるな。
- ScreenUpdatingの制御: `Application.ScreenUpdating = False` は基本だが、それ以上に重要なのは「計算モードの切り替え」だ。`Application.Calculation = xlCalculationManual` にし、計算のタイミングをエンジンの制御下に置け。
- Windows APIの活用: 高精度なタイマー制御や、大規模なデータ構造を扱う際、VBAネイティブのループでは限界がある。`Kernel32`の`GetTickCount`等を用いて、処理のボトルネックをミリ秒単位で計測する習慣をつけろ。
—
2. 負荷平準化ロジックの核:依存関係の再帰的走査
タスクの依存関係は「有向非巡回グラフ(DAG)」として扱う必要がある。特定のタスクを後ろ倒しにした際、その「後続タスク(Successors)」も芋づる式にシフトさせなければならない。
実装の骨子(簡易スケジューリングロジック)
‘ リソース負荷に基づき、タスクをシフトさせるコアエンジン
Public Sub LevelingEngine(ByVal TaskID As Long, ByVal DelayDays As Long)
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets(“WBS”)
Dim tRow As Range
‘ 1. オブジェクトの安全な参照
Set tRow = FindTaskRow(TaskID) ‘ IDから行を特定する高速検索関数
‘ 2. 開始日を更新
tRow.Cells(1, COL_START_DATE).Value = tRow.Cells(1, COL_START_DATE).Value + DelayDays
‘ 3. 後続タスクへの伝播(再帰呼び出し)
Dim successorID As Variant
For Each successorID In GetSuccessors(TaskID)
‘ 再帰的に依存関係をシフトさせる
LevelingEngine CLng(successorID), DelayDays
Next successorID
‘ メモリ最適化:オブジェクトの解放
Set tRow = Nothing
End Sub
—
3. レガシー環境でのパフォーマンス最適化
Excel VBAは単一スレッドで動作する。数千行のWBSを処理する際、毎回セルにアクセスすれば、オーバーヘッドでシステムは停止する。
- 配列への全展開: 処理対象のWBS範囲を一度 `Variant` 配列にロードせよ。メモリ上で計算を行い、最後に一括で `Range.Value` に書き戻す。これだけで処理速度は100倍以上向上する。
- 依存関係のインデックス化: 毎回 `VLOOKUP` や `Find` メソッドを使うのは素人の仕事だ。事前に `Scripting.Dictionary` を使い、`TaskID` と `RowNumber` をマッピングした辞書オブジェクトをメモリ上に保持せよ。これにより、検索コストを $O(n)$ から $O(1)$ に低減できる。
—
4. シニアエンジニアへの戒め:システム間連携の思想
このエンジンは、単体で完結させるべきではない。
将来的にSAPやkintoneといった外部APIと連携することを前提とするなら、データ層とビジネスロジック層を分離せよ。VBAのクラスモジュールを駆使し、`Task`オブジェクトを定義するのだ。
‘ Class: TaskObject
Private pID As Long
Private pStartDate As Date
Private pResourceID As String
Public Property Let ID(val As Long): pID = val: End Property
‘ …以下プロパティ定義
このようにデータ構造をカプセル化しておくことで、将来的にVBAをC#(.NET)のDLLへと移行する際、ロジックをほぼそのまま移植できる。
—
結論:コードは「資産」であれ
自動化とは、ただ楽をすることではない。「人間が判断に迷う複雑な制約を、論理という名の定規で正すこと」である。
リソースの負荷平準化は、プロジェクトの生存率を左右する最重要アルゴリズムだ。泥臭いセル操作の裏側に、厳密なメモリ管理とグラフ理論の知見を詰め込め。それが、レガシー環境を支配する唯一の道だ。
貴殿の書くコードが、明日のプロジェクトを救うことを期待している。健闘を祈る。
