【入門編】プロ向け:制約条件の「強制解除」によるスケジュール最適化とボトルネック解消 – Project VBA解析バイブル

スポンサーリンク
スポンサーリンク

スケジュールの「鎖」を解き放て:Project VBAで制約条件を制御し、真の最短工期を導き出す極意

こんにちは。現場の最前線で複雑なプロジェクトの自動化を設計している、あなたの先輩エンジニアです。

「マクロの記録」を使って、なんとか作業を自動化できた。そんな喜びを感じている方も多いでしょう。でも、もしあなたが「プロジェクトの遅延を防ぎたい」「もっと効率的なスケジュールを提案したい」と願うなら、次に乗り越えるべき壁があります。

それは、「人間が手入力した制約条件(Constraints)が、スケジュールの計算エンジンを邪魔している」という問題です。

今日は、一歩上のエンジニアへとステップアップするために、Project VBAの核心部である「制約条件の制御」と、それを利用した「スケジュール最適化アルゴリズム」の考え方を伝授します。ここをクリアすれば、あなたの作るツールは「ただの自動化」から「意思決定を支援するシステム」へと進化しますよ。

1. なぜスケジュールは「動かない」のか?

Microsoft Projectの凄さは、タスク間の依存関係(先行・後続)に基づいて、計算エンジンが自動的に日付を割り当てる点にあります。

しかし、現場ではよくこんなことが起こります。

  • 「この作業は、絶対に〇月〇日に始めなければならない」
  • 「このタスクは、遅くともこの日には終わらせておきたい」

これらを管理するために、ユーザーはタスクに「制約条件(Constraint)」を設定します。例えば「開始指定(Must Start On)」などです。

ここが落とし穴!

制約条件は、計算エンジンに対する「命令(強制力)」です。
依存関係がどれほど完璧でも、強力な制約条件(鎖)が一つあるだけで、プロジェクト全体のスケジュールは、その「鎖」に引きずられて不自然に伸びてしまいます。

プロフェッショナルなエンジニアは、こう考えます。
「今見えている終了日は、本当の最短終了日か? それとも、誰かが設定した制約条件に縛られた『偽の姿』なのか?」

2. 制約条件の種類を理解する(プロへの第一歩)

VBAを操る前に、まずはProjectが持っている「鎖」の種類を知っておきましょう。`Task.ConstraintType` プロパティで取得できます。

定数名 意味 エンジンへの影響
`pjTaskConstraintNone` 制約なし (ASAP/ALAP) 理想的な状態。 依存関係に従って自由に動く。
`pjTaskConstraintStartNoEarlierThan` 開始前指定 「これより前には始められない」という緩い制約。
`pjTaskConstraintMustStartOn` 開始指定 強力な制約。 指定日以外には絶対に動かない。
`pjTaskConstraintStartNoLaterThan` 開始後指定 「これより遅れてはならない」という制約。

「マクロの記録」では、単に日付を書き換えるだけのコードになりがちです。しかし、本物のエンジニアは、この `ConstraintType` 自体を操作します。

3. 最適化アルゴリズムの実装:制約の「一時解放」

では、いよいよ本題です。
「現在の制約条件をバックアップし、一度すべてを『制約なし(ASAP)』に解放して最短期間を算出し、その後、元の状態に戻す」という、プロ仕様の最適化ロジックを実装してみましょう。

実装のイメージ図

1. 【調査】 すべてのタスクをスキャンし、制約があるタスクを特定する。
2. 【保存】 制約の種類と日付をメモリ(Collection)に退避させる。
3. 【解放】 制約を `pjTaskConstraintNone` に書き換え、再計算を実行。
4. 【算出】 解放された状態での「プロジェクト終了日」を記録。
5. 【復元】 保存しておいたデータを使って、元の制約を正確に再設定する。

実践的なVBAコード

Option Explicit

' 制約情報を保持するためのカスタム型
Private Type ConstraintInfo
    TaskID As Long
    CType As PjConstraintType
    CDate As Date
End Type

Sub OptimizeScheduleByReleasingConstraints()
    Dim t As Task
    Dim originalConstraints As New Collection
    Dim info As ConstraintInfo
    Dim theoreticalEnd As Date
    Dim currentEnd As Date
    
    ' 現在のプロジェクト終了日を記録
    currentEnd = ActiveProject.ProjectFinish
    
    On Error GoTo ErrorHandler
    
    ' --- STEP 1 & 2: 制約の特定とバックアップ ---
    For Each t In ActiveProject.Tasks
        ' 空のタスク(削除済みなど)はスキップ
        If Not t Is Nothing Then
            ' 制約が「なし(None)」以外の場合、バックアップ対象とする
            If t.ConstraintType <> pjTaskConstraintNone Then
                info.TaskID = t.ID
                info.CType = t.ConstraintType
                info.CDate = t.ConstraintDate
                
                ' Collectionに構造体を格納(IndexをTaskIDにすると復元が楽)
                originalConstraints.Add info, CStr(t.ID)
                
                ' --- STEP 3: 制約の解放 ---
                ' 制約を「なし」に変更することで、計算エンジンを自由にさせる
                t.ConstraintType = pjTaskConstraintNone
            End If
        End If
    Next t
    
    ' --- STEP 4: 再計算と最短終了日の算出 ---
    ' 全ての制約を解いた状態で、プロジェクト全体を再計算
    ActiveProject.Calculate
    theoreticalEnd = ActiveProject.ProjectFinish
    
    ' 結果の表示
    MsgBox "【最適化シミュレーション完了】" & vbCrLf & _
           "現在の終了日: " & currentEnd & vbCrLf & _
           "理論上の最短終了日: " & theoreticalEnd & vbCrLf & vbCrLf & _
           "差分: " & DateDiff("d", theoreticalEnd, currentEnd) & " 日", vbInformation, "分析結果"

    ' --- STEP 5: 制約の復元 ---
    ' ユーザーが元のスケジュールに戻れるよう、保存した情報を書き戻す
    Dim item As Variant
    For Each item In originalConstraints
        Set t = ActiveProject.Tasks(CStr(item.TaskID))
        t.ConstraintType = item.CType
        t.ConstraintDate = item.CDate
    Next item
    
    ' 最終的な再計算
    ActiveProject.Calculate
    
    MsgBox "元の制約条件を復元しました。", vbOKOnly, "完了"
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました: " & Err.Description, vbCritical
    ' エラー時も可能な限り復元を試みる(実務ではここが重要!)
    Resume Next
End Sub

4. プロが教える、陥りやすいエラーと回避策

このコードを動かす際、あるいは自分で改良する際に、以下の3点に注意してください。これを知っているだけで、デバッグの時間が劇的に減ります。

① 「空のタスク」の罠

`For Each t In ActiveProject.Tasks` でループを回すと、ユーザーが削除したタスク(メモリ上には残っている)に当たることがあります。必ず `If Not t Is Nothing Then` で存在チェックをしてください。これを忘れると `Object Required` エラーで止まります。

② `ConstraintDate` の扱い

制約の種類(`ConstraintType`)だけを変えても、日付(`ConstraintDate`)が残っていると、計算エンジンが混乱することがあります。必ず「タイプ」と「日付」をセットで操作する癖をつけましょう。

③ 処理の重み(パフォーマンス)

タスクが数千件ある巨大なプロジェクトで、ループの中で毎回 `ActiveProject.Calculate` を呼び出すと、Excelがフリーズしたようになります。
「計算は、全ての制約を解放し終えた後に、たった一度だけ行う」。これが、高速なツールを作る鉄則です。

最後に:エンジニアとしての視点

今回紹介した手法は、単に「日付を動かす」ためのものではありません。
「人間が設定した制約(意図的な制限)」と「プロジェクトの物理的な限界(計算エンジンによる理論値)」を切り分けて考えるための、分析的なアプローチです。

このロジックを応用すれば、「どのタスクの制約を解除すれば、プロジェクト全体が最短になるか?」という、より高度なボトルネック分析ツールを作ることも可能です。

一歩ずつ、コードの裏側にある「ロジック」を理解していけば、必ずProject VBAを自由自在に操れるようになります。応援していますよ。頑張りましょう!

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