「Excel VBAが遅すぎて、フリーズ寸前…。もう使いたくない!」
そんな経験、ありませんか?
本記事では10万セル規模の大量データ処理を題材に、定番の高速化テクニック5つを実測データ付きで比較します。
なぜVBAは遅くなるのか?
VBAで大量のセルにアクセスする処理を組むと、裏で以下のような余計な処理が走ります。
- セル描画のたびに画面更新
- 式変更のたびに再計算が走る
- Worksheet_Changeなどのイベントが毎回発火する
このような処理が積み重なることで、実行時間が激増してしまいます。
高速にする5つのテクニック
高速化テクニック | 効果レベル※ | 解説 |
---|---|---|
無駄なSelectをやめる | ★★★★☆ | 不要な選択操作を排除して処理を簡素化 |
Application.ScreenUpdating = False |
★★★☆☆ | 画面描画を一時停止 |
Application.Calculation = xlCalculationManual |
★★★☆☆ | 再計算を一時停止 |
配列で一括書き込み | ★★★★★ | 一括でセルに出力 |
Withブロックで命令まとめる | ★☆☆☆☆ | 同じオブジェクトへの命令を省略化 |
※今回の計測同様の処理に適用した場合の効果レベルです
計測
本記事では、上記のテクニックを個別に実施しその効果を確認していきます。
また最後に全てのテクニックを適用したパターンについても確認します。
計測環境
今回は以下の環境での計測を行います。
やや古い環境ですが、ご参考になれば幸いです。
- Excel : 2016
- OS : Windows 11
- CPU : Intel(R) Core i7-8700K
- メモリ : 32 GB
実行時間に関する注意点
実行速度はPCスペックや他ソフトの動作状況により変動するため、実行結果の数値はあくまで参考値としてご覧ください。
再現する際は、複数回の平均を推奨します。
① 無駄なSelectをやめる
選択セルを変更すると、Excelは画面を都度更新します。
この更新が多くなると負荷が高くなり、描画処理が遅くなります。
値の出力はSelectを使わなくても実行できるため為、Selectを無くすことで高速化を図ります。
計測処理
クリックでコードを表示
Sub Test_NonSelect()
Dim startTime As Double, endTime As Double
Dim i As Long, j As Long
' --- 高速化なし(Selectあり) ---
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
Cells(i, j).Select ' 出力するセルを選択
Cells(i, j).Value = i * j ' 「行数×列数」の値を出力
Next j
Next i
endTime = Timer
Debug.Print "[Selectあり] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
' --- 高速化(Selectなし) ---
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
Cells(i, j).Value = i * j ' 「行数×列数」の値を出力
Next j
Next i
endTime = Timer
Debug.Print "[Selectなし] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
End Sub
計測結果
上記の処理を10回実行した結果を以下の表にまとめました。
Selectを行わないだけで約28倍の速度で処理を行えています。
回数 | Selectあり(秒) | Selectなし(秒) | 効果(倍率) |
---|---|---|---|
1 | 98.002 | 3.445 | 28.45 |
2 | 103.252 | 3.463 | 29.82 |
3 | 104.541 | 3.621 | 28.87 |
4 | 104.451 | 3.539 | 29.51 |
5 | 104.807 | 3.568 | 29.37 |
6 | 95.207 | 3.477 | 27.38 |
7 | 101.193 | 3.500 | 28.91 |
8 | 95.480 | 3.516 | 27.16 |
9 | 97.410 | 3.566 | 27.32 |
10 | 96.199 | 3.451 | 27.88 |
平均 | 100.0542 | 3.5146 | 28.47 |
② 画面更新の停止:ScreenUpdating
値を入れたりするたびに、Excelは画面を都度更新します。
この更新が多くなると負荷が高くなり、描画処理が遅くなります。
処理の前にApplication.ScreenUpdating = False
とすることで、描画処理を一時停止させ高速化を図ります。
計測処理
クリックでコードを表示
Sub Test_ScreenUpdating()
Dim startTime As Double, endTime As Double
Dim i As Long, j As Long
' --- 高速化なし(画面更新あり) ---
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
Cells(i, j).Select ' 出力するセルを選択
Cells(i, j).Value = i * j ' 「行数*列数」の値を出力
Next j
Next i
endTime = Timer
Debug.Print "[画面更新あり] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
' --- 高速化(画面更新なし) ---
Application.ScreenUpdating = False ' 描画処理を一時停止
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
Cells(i, j).Select ' 出力するセルを選択
Cells(i, j).Value = i * j ' 「行数*列数」の値を出力
Next j
Next i
Application.ScreenUpdating = True ' ← 元に戻すのを忘れない!
endTime = Timer
Debug.Print "[画面更新なし] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
End Sub
計測結果
上記の処理を10回実行した結果を以下の表にまとめました。
描画処理を一時停止することで約12倍の速度で処理を行えています。
回数 | 画面更新あり(秒) | 画面更新なし(秒) | 効果(倍率) |
---|---|---|---|
1 | 96.572 | 7.799 | 12.38 |
2 | 98.453 | 7.971 | 12.35 |
3 | 97.314 | 7.949 | 12.24 |
4 | 102.188 | 7.795 | 13.11 |
5 | 96.209 | 7.900 | 12.18 |
6 | 96.984 | 8.006 | 12.11 |
7 | 107.492 | 8.189 | 13.13 |
8 | 97.143 | 7.840 | 12.39 |
9 | 96.730 | 7.889 | 12.26 |
10 | 97.324 | 7.918 | 12.29 |
平均 | 98.6409 | 7.9256 | 12.44 |
③ 自動計算の停止:Calculation
セルに数式を入れると、Excelは自動的に再計算します。
この再計算が何千回も繰り返されると大きな負担になります。
Application.Calculation = xlCalculationManual
で再計算を一時的に停止して高速化を図ります。
なお、数式の単純な計算式ほどこのテクニックの効果は薄くなります。
計測処理
クリックでコードを表示
Sub Test_Calculation()
Dim startTime As Double, endTime As Double
Dim i As Long, j As Long
' --- 高速化なし(自動計算あり) ---
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
Cells(i, j).Formula = "=" & i & "*" & j & "*2" ' 「行数*列数*2」の式を出力
Next j
Next i
endTime = Timer
Debug.Print "[自動計算あり] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
' --- 高速化(自動計算なし) ---
Application.Calculation = xlCalculationManual ' 自動計算の停止
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
Cells(i, j).Formula = "=" & i & "*" & j & "*2" ' 「行数*列数*2」の式を出力
Next j
Next i
Application.Calculation = xlCalculationAutomatic ' ← 元に戻すのを忘れない!
endTime = Timer
Debug.Print "[自動計算なし] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
End Sub
計測結果
上記の処理を10回実行した結果を以下の表にまとめました。
自動計算を一時停止することで約10倍の速度で処理を行えています。
回数 | 自動計算あり(秒) | 自動計算なし(秒) | 効果(倍率) |
---|---|---|---|
1 | 63.104 | 5.510 | 11.45 |
2 | 55.648 | 5.600 | 9.94 |
3 | 61.150 | 5.494 | 11.13 |
4 | 55.098 | 5.555 | 9.92 |
5 | 59.361 | 6.416 | 9.25 |
6 | 59.111 | 5.570 | 10.61 |
7 | 54.748 | 5.527 | 9.91 |
8 | 58.980 | 5.525 | 10.68 |
9 | 54.201 | 5.697 | 9.51 |
10 | 55.211 | 5.918 | 9.33 |
平均 | 57.6612 | 5.6812 | 10.17 |
④ 配列で一括書き込み
大量のデータを各セルに随時出力していくと、ExcelとVBA間のやりとりが頻繁になり非常に遅くなります。
一度配列に値をまとめてから、一括で書き込むことで劇的に速くなります。
計測処理
クリックでコードを表示
Sub Test_UseArray()
Dim startTime As Double, endTime As Double
Dim i As Long, j As Long
' --- 高速化なし(遂次出力) ---
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
Cells(i, j).Value = i * j ' 随時「行数*列数」の値を出力
Next j
Next i
endTime = Timer
Debug.Print "[遂次出力] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
' --- 高速化(配列一括書き込み) ---
Cells.Clear
startTime = Timer
' データ準備
Dim data(1 To 10000, 1 To 10) As Variant
For i = 1 To 10000
For j = 1 To 10
data(i, j) = i * j ' 配列に出力内容(「行数*列数」の値)を格納
Next j
Next i
Range("A1").Resize(10000, 10).Value = data ' 配列を使って一括出力
endTime = Timer
Debug.Print "[配列一括] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
End Sub
計測結果
上記の処理を10回実行した結果を以下の表にまとめました。
配列で一括出力することで約35倍の速度で処理を行えています。
回数 | 遂次出力(秒) | 配列一括(秒) | 効果(倍率) |
---|---|---|---|
1 | 3.730 | 0.094 | 39.68 |
2 | 3.684 | 0.102 | 36.12 |
3 | 3.734 | 0.109 | 34.26 |
4 | 3.703 | 0.109 | 33.97 |
5 | 3.762 | 0.113 | 33.29 |
6 | 3.719 | 0.094 | 39.56 |
7 | 3.652 | 0.113 | 32.32 |
8 | 3.684 | 0.109 | 33.80 |
9 | 4.223 | 0.113 | 37.37 |
10 | 3.699 | 0.105 | 35.23 |
平均 | 3.759 | 0.1061 | 35.56 |
⑤ Withブロックで命令まとめる
同一のセルに値の出力や書式の設定等を行う場合、毎回オブジェクトを指定すると何度も同じオブジェクトにアクセスすることになり処理が遅くなります。
With ~ End With
を使って1回だけオブジェクトの取得し、
複数プロパティをまとめて変更することで命令の重複を省略しつつ高速化を図ります。
計測処理
クリックでコードを表示
Sub Test_UseWith()
Dim startTime As Double, endTime As Double
Dim i As Long, j As Long
Dim cell As Range
' --- 高速化なし(Withなし) ---
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
' 毎回対象のオブジェクト達にアクセスして更新
Cells(i, j).Value = i * j
Cells(i, j).Interior.Color = RGB(255, 255, 153)
Cells(i, j).Font.Bold = True
Cells(i, j).Font.Color = vbRed
Next j
Next i
endTime = Timer
Debug.Print "[Withなし] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
' --- 高速化(Withあり) ---
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
Set cell = Cells(i, j)
' Withを使用して一括更新
With cell
.Value = i * j
.Interior.Color = RGB(255, 255, 153)
With .Font
.Bold = True
.Color = vbRed
End With
End With
Next j
Next i
endTime = Timer
Debug.Print "[Withあり] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
End Sub
計測結果
上記の処理を10回実行した結果を以下の表にまとめました。
Withでの効果は1.06倍となりました。
効果は限定的ですが、記述の簡潔さ・可読性向上にも寄与します。
回数 | Withなし(秒) | Withあり(秒) | 効果(倍率) |
---|---|---|---|
1 | 19.090 | 17.074 | 1.12 |
2 | 17.074 | 19.559 | 0.87 |
3 | 19.715 | 19.109 | 1.03 |
4 | 19.746 | 18.656 | 1.06 |
5 | 19.895 | 18.617 | 1.07 |
6 | 21.180 | 19.672 | 1.08 |
7 | 19.855 | 18.477 | 1.07 |
8 | 19.883 | 18.535 | 1.07 |
9 | 20.039 | 18.582 | 1.08 |
10 | 19.777 | 18.520 | 1.07 |
平均 | 19.426 | 18.274 | 1.06 |
5つすべてを適用してみた
最後に、今回紹介した5つのテクニックを全てを適用した場合の効果を確認します。
クリックでコードを表示
Sub Test_AllTechniquesCombined()
Dim startTime As Double, endTime As Double
Dim i As Long, j As Long
' --- 高速化なし ---
Cells.Clear
startTime = Timer
For i = 1 To 10000
For j = 1 To 10
Cells(i, j).Select
Cells(i, j).Formula = "=" & i & "*" & j & "*2"
Cells(i, j).Interior.Color = RGB(255, 255, 153)
Cells(i, j).Font.Bold = True
Cells(i, j).Font.Color = vbRed
Next j
Next i
endTime = Timer
Debug.Print "[高速化なし] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
' --- 高速化あり(全テクニック使用) ---
Dim data(1 To 10000, 1 To 10) As Variant
Dim cell As Range
Application.ScreenUpdating = False ' 描画処理を一時停止
Application.Calculation = xlCalculationManual ' 自動計算を一時停止
Cells.Clear
startTime = Timer
' データ準備
For i = 1 To 10000
For j = 1 To 10
data(i, j) = "=" & i & "*" & j & "*2"
Next j
Next i
' Selectなし、配列一括書き込み
Range("A1").Resize(10000, 10).Formula = data
' 一括書き込み後にWithで書式設定
For i = 1 To 10000
For j = 1 To 10
Set cell = Cells(i, j)
With cell
.Interior.Color = RGB(255, 255, 153)
With .Font
.Bold = True
.Color = vbRed
End With
End With
Next j
Next i
endTime = Timer
Application.Calculation = xlCalculationAutomatic ' 自動計算を戻す
Application.ScreenUpdating = True ' 描画処理を戻す
Debug.Print "[全テクニック適用] 時間: " & Format(endTime - startTime, "0.000") & " 秒"
End Sub
計測結果
上記の処理を10回実行した結果を以下の表にまとめました。
今回紹介したテクニックをすべて使用した場合のの効果は約34.12倍となりました。
回数 | 高速化なし(秒) | 高速化あり(秒) | 効果(倍率) |
---|---|---|---|
1 | 363.211 | 9.914 | 36.64 |
2 | 331.008 | 9.883 | 33.49 |
3 | 331.250 | 9.805 | 33.78 |
4 | 322.414 | 9.680 | 33.31 |
5 | 313.719 | 9.492 | 33.05 |
6 | 329.688 | 9.570 | 34.45 |
7 | 340.563 | 9.516 | 35.79 |
8 | 324.000 | 9.484 | 34.16 |
9 | 319.750 | 9.617 | 33.25 |
10 | 321.656 | 9.664 | 33.28 |
平均 | 329.7259 | 9.6625 | 34.12 |
結果まとめ
高速化テクニック | Before | After | 効果 | 効果レベル(速度) |
---|---|---|---|---|
無駄なselectをやめる | 100.05 秒 | 3.51 秒 | 約28.5倍 | ★★★★☆ |
画面更新の停止 (ScreenUpdating) |
98.64 秒 | 7.93 秒 | 約12.4倍 | ★★★☆☆ |
自動計算の停止 (Calculation) |
57.66 秒 | 5.68 秒 | 約10.2倍 | ★★★☆☆ |
配列で一括書き込み | 3.76 秒 | 0.11 秒 | 約35.6倍 | ★★★★★ |
Withブロックで命令まとめる | 19.43 秒 | 18.52 秒 | 約1.1倍 | ★☆☆☆☆ |
5つ全て | 329.73 秒 | 9.66 秒 | 約34.1倍 | ★★★★★ |
総まとめ
- VBAの遅さの原因は、画面更新や都度アクセスなどの余計な処理にあり。
- それらを止めたりまとめたりすることで、処理速度は10〜30倍以上に向上することも。
- 特に効果が大きかったのは、配列による一括書き込みとSelectの排除。
-
With
のような小技も、繰り返し処理では意外と効く場面がある。 - 処理内容によって効くテクニックは異なるため、実務に合わせて使い分けるのが重要。
その他の高速化テクニック
本記事で紹介した5つのテクニックは、VBA高速化の定番かつ汎用的な方法です。
今後以下のような追加テクニックも別記事で紹介予定です。
- イベントの一時無効化(Worksheet_Changeイベントなどのトリガーを停止)
- DoEventsの効果と注意点
- VBEの最適化設定(オプション設定、コードモジュール整理など)
- オブジェクト変数の使いまわし、ネストの簡素化
気になる内容があればフォロー・ストックしていただけると嬉しいです!