こんにちは!現場でバリバリVBAを書いていると、「あれ、なんだかマクロの動きがやたらと遅いな……」と頭を抱える瞬間に出会いませんか?
特にMS Projectを相手にする大規模案件では、数千件にも及ぶリソースやタスクを扱うことになります。ここで「画面を表示したまま、1行ずつリソースを操作する」なんてコードを書いた日には、コーヒーを飲み終えても終わらない、なんて悲劇が起きてしまいます。
今回は、数千件のリソース情報を爆速で読み込み、プロジェクトのコストや稼働率をスマートに管理するための「配列処理の極意」を伝授します。ここをクリアすれば、あなたもVBAの初級者を卒業し、アーキテクトへの第一歩を踏み出せますよ。しっかりついてきてくださいね!
—
1. なぜ、MS ProjectのVBAは遅くなるのか?
まずは敵を知ることから始めましょう。
Project VBAで一番やってはいけないアンチパターン、それは「ワークスペース(画面)とオブジェクトを行き来しながら1件ずつ処理すること」です。
【遅い処理のイメージ:1件ごとにDOM/オブジェクトを叩く】
VBA ──(重い通信)──> Project画面 ──> リソース1件取得
VBA <──(重い通信)── Project画面 <── 戻り値
※これを数千回繰り返すため、激重になります。
MS Projectの裏側では、オブジェクト(Resourceなど)にアクセスするたびに、Excel以上に重たい内部処理や画面の再描画(あるいはそれに準ずる更新処理)が発生しています。数千件のリソースに対してこれをやると、あっという間にフリーズ寸前の状態に陥ります。
解決の切り札:メモリ上(配列)での高速処理
そこで登場するのが、「最初にデータを全部メモリ(配列)に読み込み、VBAの内部だけで計算を完結させて、最後にドカンと書き戻す」というテクニックです。
【速い処理のイメージ:メモリ内で完結】
Project画面 ──(一括取得)──> VBAのメモリ(配列) [高速!]
↓
VBA内でサクサク計算
↓
Project画面 <──(一括反映)── VBAのメモリ(配列) [一瞬!]
このアプローチを取り入れるだけで、処理時間が数分から数秒へと劇的に短縮されます。
—
2. 実践!数千件のリソース情報を爆速で読み込むコード
それでは、実際の現場でそのまま使えるサンプルコードを見ていきましょう。
今回は、プロジェクトに登録されているすべてのリソースの「名前」「標準単価」「最大稼働率」をメモリ上に一気に取り込み、ログに出力(または処理)するスマートなコードを書きます。
Sub SpeedUpResourceLoading()
Dim startTime As Double
startTime = Timer ‘ 処理時間計測用
‘ 画面描画やイベントを停止して爆速化の恩恵を最大化
Application.ScreenUpdating = False
Dim resCount As Long
resCount = ActiveProject.Resources.Count
If resCount = 0 Then
MsgBox “リソースが登録されていません。”, vbExclamation
Exit Sub
End If
‘ 【重要】リソース情報を格納するための動的配列を準備
‘ 配列のサイズ:(1 to 項目数, 1 to リソース総数)
Dim resData() As Variant
ReDim resData(1 To 3, 1 To resCount)
Dim r As Resource
Dim i As Long
i = 1
‘ 1. 【一括読み込み】オブジェクトから配列へデータを流し込む
‘ ここで一度だけオブジェクトにアクセスします
For Each r In ActiveProject.Resources
If Not r Is Nothing Then
resData(1, i) = r.Name ‘ リソース名
resData(2, i) = r.StandardRate ‘ 標準単価
resData(3, i) = r.MaxUnits ‘ 最大稼働率
i = i + 1
End If
Next r
‘ — ここから下は、メモリ上の配列だけで高速にデータ処理を行えます —
Dim totalCostSimulation As Double
totalCostSimulation = 0
For i = 1 to resCount
‘ 例:すべてのリソースの標準単価を合計してみる(メモリ上なので一瞬)
totalCostSimulation = totalCostSimulation + Val(resData(2, i))
Next i
‘ 画面描画を復元
Application.ScreenUpdating = True
‘ 処理結果の発表
Dim endTime As Double
endTime = Timer
MsgBox “処理完了!” & vbCrLf & _
“対象リソース数: ” & resCount & “件” & vbCrLf & _
“処理時間: ” & Format(endTime – startTime, “0.00秒”) & vbCrLf & _
“単価合計(シミュレーション): ” & totalCostSimulation, vbInformation
End Sub
コードのポイント解説
1. `Application.ScreenUpdating = False` の魔法
処理の最初に画面更新を止め、最後に `True` に戻しています。これだけで余計な描画コストをカットできます。
2. `ReDim` による二次元配列の確保
あらかじめリソースの総数 (`ActiveProject.Resources.Count`) を取得し、必要なメモリ空間を一網打尽で確保します。これがメモリ効率を極限まで高めるコツです。
3. ループ内でのドットつなぎを避ける
配列に代入した後は、`r.StandardRate` のような重いオブジェクトプロパティへのアクセスを一切行わず、純粋な変数 (`resData(2, i)`) 同士で計算を行います。これが速さの秘密です。
—
3. 陥りやすい罠とエラー回避の知見
現場でこのコードを実装する際、エンジニアがよくハマる「落とし穴」がいくつかあります。事前に知っておけば怖くありません。
罠①:「Nothing」の亡霊に足元をす救われる
MS Projectのリソースコレクションには、途中に空き番(削除されたリソースの残骸など)が含まれている場合があります。
- 対策: サンプルコードの通り、`If Not r Is Nothing Then` というガード節を必ず挟みましょう。これを怠ると「オブジェクト変数が設定されていません (Error 91)」というお馴染みのエラーに直面します。
罠②:コストや稼働率の型(Type)のミスマッチ
Projectのプロパティ(`StandardRate` など)は、通貨型やVariant型など、少しクセのあるデータ型を返します。
- 対策: 配列に格納する段階、あるいは計算する段階で `Val()` 関数や `CStr()` 関数を使って明示的に型を整えておくと、後続のコスト計算やデータベースへの書き戻し時に「型が一致しません (Error 13)」エラーを防げます。
—
まとめ
いかがでしたか?今回は「大規模プロジェクトにおけるリソース情報の読み込み高速化テクニック」というテーマで、配列を使ったメモリ内処理の重要性を解説しました。
- オブジェクトへの頻繁なアクセスは悪!
- データを一度配列(メモリ)に回収してサクサク処理する!
- 画面描画のオン・オフを制する者がVBAを制す!
ここをクリアできれば、数千件規模の大規模スケジュールや複雑なリソース配分・コスト管理であっても、ストレスフリーでサクサク動く強靭なマクロを構築できるようになります。
日々の業務自動化を、ぜひワンランク上のアーキテクト視点で快適なものにしていってくださいね。あなたのVBAライフを応援しています!
