プロジェクトの「停滞」をコードで解く:VBAによる自律的リソース平準化の極意
プロジェクトマネジメントにおいて、「標準の平準化機能」は往々にしてブラックボックスだ。MS Projectの組み込み機能は強力だが、現場の泥臭い「優先順位」や「政治的な納期」を汲み取ってはくれない。
今日解説するのは、あえて標準機能に頼らず、VBAでリソースのオーバーアロケーション(過負荷)を論理的に解消するカスタム・シミュレーターの構築手法だ。
これは単なる自動化ではない。リソースの稼働率を可視化し、ボトルネックを「後ろ倒し」という戦術で解消する、エンジニアリングの意志を込めた実装である。
—
1. なぜ「標準機能」では現場を救えないのか?
標準の平準化エンジンは、しばしば「平準化」という名の下に、プロジェクトのクリティカルパスを破壊し、不可解なタスクの先送りを行う。現場が求めるのは、以下の条件を満たした平準化だ。
- 優先順位の明確化: 特定の重要タスクは絶対に動かさない。
- 閾値の管理: 稼働率100%を維持するのか、余力を考慮して80%に抑えるのか。
- 変更の履歴: なぜそのタスクが後ろ倒しされたのか、フラグとして残す。
これらを制御するためには、「データモデルの分離」が不可欠だ。Excelを単なる表計算としてではなく、リレーショナルなデータベースとして扱い、ロジックを独立させる設計思想が求められる。
—
2. アーキテクチャ設計:堅牢な平準化ロジック
平準化のコアは「時系列リソース消費量のシミュレーション」にある。以下の手順でコードを組むのが、最もバグを排し、かつ保守性を高めるアプローチだ。
1. データ抽出: タスクID、リソース名、開始日、終了日、工数を配列(Array)に読み込む。
2. 時系列マップの作成: リソースごとの日付×消費量の2次元配列を作成。
3. シミュレーション: 日付の早い順にタスクを処理し、閾値を超えた場合に開始日をインクリメント(後ろ倒し)する。
4. 書き戻し: 計算結果をシートに反映。
—
3. 実装:オーバーアロケーション解消エンジン
以下に、プロダクション環境でも耐えうる設計のコード例を提示する。ポイントは、`Range`オブジェクトへの頻繁なアクセスを避け、メモリ上で高速に演算を完結させることだ。
Option Explicit
‘ リソース平準化用シミュレーター
Public Sub ExecuteResourceLeveling()
‘ 設定: 閾値(例: 1.0 = 100%)
Const MAX_CAPACITY As Double = 1.0
‘ メモリ上でタスクデータを配列に格納(高速化の定石)
Dim taskData As Variant
taskData = Range(“A2:E” & Cells(Rows.Count, 1).End(xlUp).Row).Value
‘ リソースの消費量を管理するディクショナリ(Key: リソース名_日付, Value: 消費量)
Dim consumptionMap As Object
Set consumptionMap = CreateObject(“Scripting.Dictionary”)
Dim i As Long
Dim res As String, startDate As Date, effort As Double, days As Long
‘ 優先順位に基づいてソート済みと仮定してループ
For i = 1 To UBound(taskData, 1)
res = taskData(i, 2)
startDate = taskData(i, 3)
effort = taskData(i, 4) ‘ 1日あたりの工数
‘ ロジック: リソースが溢れていたら開始日をずらす
Do While IsOverloaded(res, startDate, effort, consumptionMap, MAX_CAPACITY)
startDate = startDate + 1
Loop
‘ 消費量をマップに書き込む(確定)
Call UpdateConsumption(res, startDate, effort, consumptionMap)
‘ 結果をセルに反映
Cells(i + 1, 6).Value = startDate ‘ 調整後の開始日
Next i
MsgBox “平準化シミュレーション完了”, vbInformation
End Sub
‘ 負荷判定関数
Private Function IsOverloaded(res As String, sDate As Date, effort As Double, map As Object, limit As Double) As Boolean
Dim key As String
key = res & “_” & Format(sDate, “yyyy/mm/dd”)
If map.Exists(key) Then
If map(key) + effort > limit Then IsOverloaded = True: Exit Function
End If
IsOverloaded = False
End Function
‘ 負荷更新プロシージャ
Private Sub UpdateConsumption(res As String, sDate As Date, effort As Double, map As Object)
Dim key As String
key = res & “_” & Format(sDate, “yyyy/mm/dd”)
map(key) = map(key) + effort
End Sub
—
4. 実務で「失敗しない」ためのプロフェッショナルな知見
- 「再計算」は必ず別シートに: 元の計画を破壊してはいけない。常に「調整前」と「調整後」の列を並べること。これにより、平準化がどれだけプロジェクト納期を遅延させたかというKPIを経営層に提示できる。
- データベース連携の罠: もしデータがSQL ServerやSharePointにある場合、VBAで直接ループを回すのは愚策だ。必ず一度Excelのテーブルまたは配列に抽出し、メモリ内で処理してからバルク更新(またはCSV出力)を行うこと。IO負荷を減らすことが、大規模プロジェクトを動かす鍵だ。
- 保守性の確保: リソース名や閾値はハードコードせず、必ず「設定シート」から動的に取得する設計にせよ。
最後に:エンジニアとしての矜持
VBAはレガシーな言語と揶揄されることもある。しかし、現場の複雑な業務ルールをコードという形に落とし込み、人間にしかできなかった判断を自動化する力は、他のどんな言語にも負けない。
「平準化」とは単なる計算ではない。それは、プロジェクトという生き物に対して、リソースという血流を最適に循環させるための調律なのだ。ぜひ、このロジックを自身の現場に合わせて最適化し、混沌としたプロジェクトに秩序をもたらしてほしい。
