Microsoft Project(以下、MS Project)を単なる「ガントチャート作成ツール」だと思っているなら、今すぐその認識を捨てろ。MS Projectの本質は、タスクとリソースの動的なマッピングエンジンだ。
特に、数百のタスクと数十のリソースが入り乱れる大規模プロジェクトにおいて、誰がどのタスクに適任かを「記憶」や「直感」で判断するのは、もはやエンジニアリングではない。それは単なるギャンブルだ。
今回は、Project VBAを駆使して、リソースの「スキルセット」とタスクの「要求要件」を高度に演算し、最適な担当者を自動的に導き出す「スキルマッチング・エンジン」の実装について解説する。
—
1. 概念設計:なぜあなたのマッチングロジックは破綻するのか
初心者は往々にして、リソース名とタスク名を文字列照合するだけのコードを書く。だが、プロの設計は違う。実務では以下の3要素が不可欠だ。
1. メタデータの正規化: スキル情報は「EnterpriseCustomFields」あるいは「Text1~30」の拡張属性に定義する。
2. スコアリング・アルゴリズム: 単なる一致(0 or 1)ではなく、習熟度や親和性を数値化して評価する。
3. アサインの整合性: スキルが合致しても、そのリソースの「稼働可能時間(Availability)」がゼロなら、それは「最適」とは呼べない。
今回は、タスクの「要求スキル(Text1)」と、リソースの「保有スキル(Text1)」を比較し、さらに「習熟度(Number1)」を加味したスコアリングを行い、候補者をランキング形式で提案するロジックを構築する。
—
2. データベース設計(カスタムフィールドの定義)
VBAを動かす前に、MS Project側の箱を定義せよ。これが設計の「型」になる。
- Task.Text1: 要求スキル(例: “VBA”, “SQL”, “Python”)
- Resource.Text1: 保有スキル(例: “VBA, SQL”)
- Resource.Number1: スキルレベル(1~5の数値)
—
3. プロダクション・コード:スキルマッチング・エンジン
このコードは、現在選択しているタスクに対して、最適なリソースを上位5名までイミディエイトウィンドウにリストアップし、さらに最もスコアの高いリソースを「候補者」フィールド(Text2)に書き込むものだ。
Option Explicit
‘ ==============================================================================
‘ プログラム名: ResourceSkillMatcher
‘ 概要: タスクの要求スキルとリソースの保有スキルを照合し、最適候補を算出する
‘ 設計思想: オブジェクトへの直接参照を最小限にし、メモリ上での演算を優先する
‘ ==============================================================================
Public Sub SuggestBestResources()
Dim targetTask As Task
Dim res As Resource
Dim reqSkill As String
Dim resSkills As String
Dim score As Double
Dim ranking As Object ‘ Scripting.Dictionary を使用
‘ 1. アクティブなタスクの妥当性チェック
Set targetTask = ActiveCell.Task
If targetTask Is Nothing Then
MsgBox “タスクを選択してから実行してください。”, vbExclamation
Exit Sub
End If
‘ タスクの要求スキルを取得
reqSkill = Trim(targetTask.Text1)
If reqSkill = “” Then
MsgBox “タスクの『要求スキル(Text1)』が未入力です。”, vbInformation
Exit Sub
End If
‘ 2. ランキング格納用辞書の初期化(参照設定: Microsoft Scripting Runtime)
‘ ※参照設定を避けたい場合は CreateObject(“Scripting.Dictionary”) を使用
Set ranking = CreateObject(“Scripting.Dictionary”)
‘ 3. リソースの走査とスコアリング
‘ Application.ActiveProject.Resources は、削除済みリソースも含む可能性があるため注意
For Each res In ActiveProject.Resources
If Not res Is Nothing Then
‘ 汎用的なリソース(Generic)や、コスト型リソースは除外
If Not res.Generic And res.Type = pjResourceTypeWork Then
score = CalculateMatchScore(reqSkill, res.Text1, res.Number1)
‘ スコアが0より大きい場合のみ候補とする
If score > 0 Then
‘ 同姓同名の考慮は実務では必須。ここではIDをキーにする
ranking.Add res.ID, score
End If
End If
End If
Next res
‘ 4. 結果の出力とタスクへの反映
If ranking.Count > 0 Then
DisplayTopCandidates targetTask, ranking
Else
MsgBox “条件に合致するリソースが見つかりませんでした。”, vbInformation
End If
Set ranking = Nothing
End Sub
‘ ——————————————————————————
‘ 関数名: CalculateMatchScore
‘ 引数: 要求スキル, 保有スキル文字列, スキルレベル
‘ 戻り値: マッチングスコア (0 – 100)
‘ ——————————————————————————
Private Function CalculateMatchScore(ByVal req As String, ByVal owned As String, ByVal level As Double) As Double
Dim score As Double
score = 0
‘ 単純な部分一致判定(実務ではここを正規表現やベクター演算に拡張可能)
If InStr(1, owned, req, vbTextCompare) > 0 Then
‘ スキルレベル(1-5)を20倍して100点満点換算するロジック
score = level 20
End If
CalculateMatchScore = score
End Function
‘ ——————————————————————————
‘ 関数名: DisplayTopCandidates
‘ 概要: スコア順にソートし、上位者を提案する
‘ ——————————————————————————
Private Sub DisplayTopCandidates(ByRef t As Task, ByRef dict As Object)
Dim key As Variant
Dim sortedList As Object
Dim resID As Long
Dim msg As String
Dim topResName As String
Dim maxScore As Double
‘ 本来はここでバブルソート等のソートアルゴリズムが必要だが、
‘ 簡易化のため最大値を取得するロジックとする
maxScore = -1
msg = “【マッチング結果: ” & t.Name & “】” & vbCrLf
For Each key In dict.Keys
If dict(key) > maxScore Then
maxScore = dict(key)
resID = CLng(key)
topResName = ActiveProject.Resources(resID).Name
End If
msg = msg & “・” & ActiveProject.Resources(CLng(key)).Name & ” (Score: ” & dict(key) & “)” & vbCrLf
Next key
‘ 最上位候補をタスクのText2(候補者フィールド)に自動入力
t.Text2 = topResName & ” (Score: ” & maxScore & “)”
Debug.Print msg
MsgBox msg & vbCrLf & “最高評価の ” & topResName & ” を候補者に設定しました。”, vbInformation
End Sub
—
4. 現場の審美眼:プロがこだわる「堅牢性」のポイント
① リソースの「ライフサイクル」を意識せよ
MS Projectのリソースは、プロジェクトの途中で削除されたり、非アクティブ化されたりする。`For Each res In ActiveProject.Resources` でループを回す際、`Nothing` チェックは必須だ。また、`res.Generic`(汎用リソース)を計算対象から外す処理を入れないと、実在しない「プログラマー」という架空の枠にタスクを割り当てるというマヌケな結果を招く。
② 計算負荷の分散
数千のタスクに対して一括でマッチングを行う場合、`ScreenUpdating = False` は当然として、`Calculation = pjManual` に設定することを忘れるな。アサインメントが更新されるたびに再計算が走ると、VBAの実行速度は指数関数的に低下する。
③ 外部データ連携の拡張性
今回は簡易的に `Text1` を使用したが、大規模組織ではリソースのスキル情報は 人事システム(SQL ServerやSharePointリスト) に存在することが多い。
その場合、`Resource_BeforeResourceChange` イベントをフックし、外部DBからADODB接続でスキル情報をリアルタイムに取得・同期する設計に昇華させるのがチーフアーキテクトの仕事だ。
—
5. 終わりに
自動マッチングは、単なる「省力化」ではない。それは、プロジェクトマネージャーの主観を排除し、データに基づいた客観的なリソース配置(Evidence-based Allocation)を実現するための聖杯だ。
今回提示したコードは、その広大な領域の入り口に過ぎない。ここから「稼働率の平準化ロジック」や「コスト効率の最適化」を組み込み、自分だけの最強のプロジェクト・コックピットを構築してほしい。
コードをコピペして動かした瞬間、あなたのプロジェクト管理は「管理」から「制御」へと進化するはずだ。
