【VBA】配列を使ってVBAの処理速度を10倍にする方法(コピペOK)

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

この記事でわかること

VBAの処理が遅くて困っていないだろうか。1万行のデータ処理に何分もかかる、実行中にExcelが固まる――そんな経験があるなら、この記事が役に立つ。

この記事では、VBAの処理速度を劇的に改善する 3つのテクニック を優先度順に解説する。

  1. ScreenUpdating=False(1行追加で2.5倍速)
  2. 配列で一括読み書き(25倍速、最も効果が大きい)
  3. 全テクニック併用テンプレート(40倍速、コピペですぐ使える)

すべてコピペで動くコード付き。既存のマクロにも簡単に組み込める。

どんな場面で使う?

  • 1万行以上のデータ処理マクロが何分もかかって業務に支障がある
  • セルを1つずつ操作するマクロでExcelが「応答なし」になる
  • 既存の遅いマクロを書き換えずに高速化したい
  • 大量データの集計・転記をVBAで処理しているが体感速度を改善したい

結論:配列を使えば10倍速くなる

先に結論を見せる。1万行×4列のデータを処理した場合のベンチマーク比較がこちら。

方法 1万行の処理時間 速度
セル直接操作 約8秒 ×1
ScreenUpdating=False 約3秒 ×2.5
配列で一括読み書き 約0.3秒 ×25
全テクニック併用 約0.2秒 ×40

※ 環境(PC性能・Excel版・ブック内の数式量)により数値は変動する。相対的な速度比は概ね同程度。

セル直接操作で8秒かかっていた処理が、配列を使えば0.3秒。全テクニックを併用すれば0.2秒で終わる。配列が最も効果が大きく、ScreenUpdating=FalseやCalculation=xlManualは補助的な役割。ただし数式が多いブックでは Calculation=xlManual の効果が顕著になる。

筆者の実体験: 以前、5万行の売上データを集計するマクロを作ったとき、セル直接操作で30分以上かかっていた。配列に書き換えたら3分で終わるようになった。コードの変更量は20行程度。それだけで27分の短縮になった。

なぜVBAの処理が遅くなるのか

VBAが遅くなる原因は主に3つある。

原因1:セルアクセスのオーバーヘッド

VBAからセルに1つずつアクセスするたびに、VBAとExcelの間でデータの変換処理が発生する。1万行×4列のデータなら、4万回のセルアクセスが発生する。


' これが遅い(4万回のセルアクセス)
For i = 2 To 10001
    Cells(i, 4) = Cells(i, 1) * Cells(i, 2)
Next i

原因2:画面の再描画

セルの値が変わるたびに、Excelは画面を再描画する。1万回の書き込みがあれば、1万回の画面更新が発生する。

原因3:数式の再計算

シート内に数式があると、セルに値を書き込むたびに数式が再計算される。数式が多いブックほど影響が大きい。

テクニック1:Application.ScreenUpdating = False(最も簡単)

最も手軽な高速化テクニック。たった1行追加するだけで、処理速度が約2.5倍になる。


Sub 高速化_画面更新停止()
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual

    ' ここに処理を書く

    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
End Sub

各設定の意味:

設定 効果
ScreenUpdating = False 画面の再描画を停止。処理中に画面がチラつかなくなる
Calculation = xlCalculationManual 数式の自動再計算を停止。書き込みのたびの再計算を防ぐ

注意: 処理が終わったら必ず True / xlCalculationAutomatic に戻すこと。戻し忘れるとExcelの挙動がおかしくなる。エラーで処理が中断した場合も戻らないので、本番環境ではテクニック3のテンプレート(On Error GoTo付き)を使うことを推奨する。もしScreenUpdating=Falseのまま止まってしまった場合は、VBEのイミディエイトウィンドウで Application.ScreenUpdating = True を実行すれば復帰できる。

筆者の体験: この2行を追加するだけで、3000行の色分け処理が12秒から5秒になった。コードを1行も変えずに2倍以上速くなるので、まずはこれを試してほしい。画面更新停止の実例は /006(行の色分け)でも使用している。

テクニック2:配列で一括読み書き(最も効果大)

高速化の本命がこれ。セル範囲を配列(メモリ上のデータ)に一括で読み込み、配列上で処理し、結果を一括で書き戻す。

なぜ速いのか

セル直接操作の場合、1万行×4列 = 4万回のセルアクセスが発生する。配列なら、読み込み1回 + 書き込み1回の 計2回 で済む。アクセス回数が2万分の1になるので、速くなるのは当然だ。

コード


Sub 高速化_配列で処理()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim arr As Variant
    Dim i As Long

    Set ws = ActiveSheet
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    ' セル範囲を配列に一括読み込み
    arr = ws.Range("A2:D" & lastRow).Value

    ' 配列上で処理(セルアクセスなし → 超高速)
    For i = 1 To UBound(arr, 1)
        ' 例:D列(4番目)にA列×B列の計算結果を入れる
        If IsNumeric(arr(i, 1)) And IsNumeric(arr(i, 2)) Then
            arr(i, 4) = arr(i, 1) * arr(i, 2)
        End If
    Next i

    ' 配列をセルに一括書き込み
    ws.Range("A2:D" & lastRow).Value = arr

    MsgBox "完了しました。"
End Sub

最終行の取得について: ws.Cells(ws.Rows.Count, 1).End(xlUp).Row はA列の最終行を取得している。A列にデータがない場合は正しく取得できない。最終行の取得方法について詳しくは /032 を参照。

コードの読み方

意味
arr = ws.Range("A2:D" & lastRow).Value A2:D最終行のデータを配列に一括読み込み
For i = 1 To UBound(arr, 1) 配列の1行目から最終行までループ
arr(i, 1) 配列のi行目・1列目(= A列の値)
arr(i, 4) = arr(i, 1) * arr(i, 2) D列にA列×B列の結果を代入
ws.Range("A2:D" & lastRow).Value = arr 配列をセルに一括書き込み

ポイント: 配列のインデックスは 1始まりarr(1, 1) が最初のデータ(A2セルの値)になる。0始まりではないので注意。

テクニック3:全テクニックを組み合わせたテンプレート

テクニック1とテクニック2を組み合わせた、実務で使える完全版テンプレート。エラー時の設定復帰処理も入っている。


Sub 高速処理テンプレート()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim arr As Variant
    Dim i As Long
    Dim startTime As Double

    Set ws = ActiveSheet
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    If lastRow < 2 Then
        MsgBox "データがありません。", vbExclamation
        Exit Sub
    End If

    startTime = Timer

    ' === 高速化設定 ===
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.EnableEvents = False

    On Error GoTo ErrorHandler

    ' === 配列に一括読み込み ===
    arr = ws.Range("A2:D" & lastRow).Value

    ' === 配列上で処理 ===
    For i = 1 To UBound(arr, 1)
        ' ★ ここに処理を書く
        If IsNumeric(arr(i, 1)) And IsNumeric(arr(i, 2)) Then
            arr(i, 4) = arr(i, 1) * arr(i, 2)
        End If
    Next i

    ' === 配列を一括書き込み ===
    ws.Range("A2:D" & lastRow).Value = arr

CleanUp:
    ' === 高速化設定を戻す(必ず実行される) ===
    Application.EnableEvents = True
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True

    MsgBox "完了(" & Format(Timer - startTime, "0.00") & "秒)"
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました:" & Err.Description, vbCritical
    Resume CleanUp
End Sub

コードのポイント

On Error GoTo ErrorHandler — エラーが発生したらErrorHandlerに飛ぶ。ErrorHandlerからCleanUpに飛ぶので、エラーが起きてもScreenUpdating=Trueなどの設定復帰が 必ず実行される。これが最も重要な安全対策。

筆者の体験: 以前、On Error GoToを入れずにマクロを実行中にエラーが出て、ScreenUpdating=Falseのまま止まったことがある。Excelの画面が真っ白になり、何も操作できなくなった。VBEのイミディエイトウィンドウで Application.ScreenUpdating = True を実行してようやく復帰できた。それ以来、高速化設定を使うときは必ずOn Error GoToを入れるようにしている。

Timer関数 — 処理前後の時刻差で経過秒数を表示する。高速化の効果を数値で実感できる。

EnableEvents = False — Worksheet_Changeなどのイベントプロシージャが設定されている場合、セルへの書き込みでイベントが発火する。Falseにしておけば余計な処理が走らない。イベントプロシージャの詳細は /008 を参照。

データ存在チェックIf lastRow < 2 Then で、ヘッダー行しかない(データがない)場合は処理を中断する。

---

なぜ配列は速いのか

VBAとExcelは別々のプログラムとして動いている。セルにアクセスするたびに、VBAとExcelの間でデータのやり取り(通信)が発生する。

セル直接操作のイメージ:


VBA → Excel「A2の値をください」 → Excel → VBA「150です」
VBA → Excel「B2の値をください」 → Excel → VBA「3です」
VBA → Excel「D2に450を書いてください」 → Excel「書きました」
(これを1万行分繰り返す = 4万回の通信)

配列のイメージ:


VBA → Excel「A2:D10001の全データをください」 → Excel → VBA「まとめて渡します」
(VBAのメモリ上で1万行分の計算を実行)
VBA → Excel「A2:D10001にまとめて書いてください」 → Excel「書きました」
(通信は2回だけ)

通信回数が4万回から2回に減る。これが配列が速い理由だ。

---

比較表:各テクニックの効果

どのテクニックをどの順番で使うべきか、まとめた。

テクニック 速度向上 導入の手軽さ おすすめの場面
ScreenUpdating=False ×2.5 1行追加するだけ まずこれを試す。すべてのマクロに入れてよい
Calculation=xlManual ×1.5(追加) 1行追加するだけ 数式が多いブックで効果大
EnableEvents=False ×1.1(追加) 1行追加するだけ Worksheet_Changeなどのイベントがある場合
配列で一括読み書き ×25 コード構造の変更が必要 1000行以上のデータ処理で効果大
全テクニック併用 ×40 テンプレートをコピペ 大量データ処理の定番パターン

判断の目安:

  • 数百行以下 → ScreenUpdating=Falseだけで十分
  • 1000行以上 → 配列の導入を検討
  • 1万行以上 → 配列 + 全テクニック併用を強く推奨

複数ファイルの統合処理(/005)や複数シートの一括処理(/015)でも、配列を組み合わせると大幅に高速化できる。

---

よくある落とし穴5選

# 症状 原因 対策
1 マクロがエラーで止まり、Excelの画面が真っ白になった ScreenUpdating=Falseのまま処理が中断した On Error GoToでエラーハンドラを設け、CleanUpラベルで必ず ScreenUpdating=True に戻す。手動復帰はVBEのイミディエイトウィンドウで Application.ScreenUpdating = True を実行
2 配列のデータが1行ずれている 配列のインデックスが1始まりなのに0始まりだと思ってコードを書いた Range.Value で取得した配列は常に1始まり。arr(1, 1) が最初のデータ。不安なら LBound(arr, 1) で下限を確認する
3 配列の書き込み後、データが消えた・余分な行が出た 書き込み先のセル範囲が配列のサイズと一致していない 書き込み前に UBound(arr, 1) で行数を確認する。書き込み範囲は「開始行 + UBound(arr, 1) - 1」で計算する。例:開始行が2なら Range("A2:D" & 2 + UBound(arr, 1) - 1)
4 マクロ実行後、シートの数式が自動計算されなくなった Calculation=xlCalculationManualのまま処理が終了した On Error GoToで必ず xlCalculationAutomatic に戻す。手動復帰は「数式」タブ→「計算方法の設定」→「自動」
5 配列内の空セルで「型が一致しません」エラーが出る 空セルはVariant配列で Empty になり、数値演算すると型不一致エラーになる IsEmpty(arr(i, 1)) または IsNumeric(arr(i, 1)) で判定してから計算する

筆者の体験: 落とし穴1は本当によくやる。特に開発中はエラーが頻発するので、On Error GoToを入れずにScreenUpdating=Falseを使うと、エラーのたびにExcelが固まる。テクニック3のテンプレートには最初からOn Error GoToを入れてあるので、コピペして使えばこの問題は起きない。

VBAで配列に読み込んだデータがずれるときの対処法

「配列のデータが1行ずれている」という場合、原因は配列のインデックスが0始まりだと思い込んでいることだ。Range.Value で取得した配列は常に1始まり(arr(1, 1) が先頭)。不安なら LBound(arr, 1)UBound(arr, 1) で範囲を確認してからループすれば安全だ。

VBAでScreenUpdating=Falseのまま画面が固まったときの対処法

「マクロがエラーで止まりExcelの画面が真っ白になった」という場合、原因はScreenUpdating=Falseが復帰されていないことだ。VBEを開き(Alt+F11)、イミディエイトウィンドウ(Ctrl+G)で Application.ScreenUpdating = True と入力してEnterを押せば復旧する。On Error GoToで必ず復帰処理を入れるのが予防策だ。

---

FAQ

Q1: 既存のマクロに配列を導入するのは大変?

基本パターンは3ステップ。(1) セル範囲を配列に読み込む (2) Cells(i, j)arr(i, j) に書き換える (3) 配列をセルに書き戻す。セル参照を配列参照に置き換えるだけなので、コード全体を書き直す必要はない。既存マクロの書き換えが難しい場合はココナラで相談できます。

Q2: ScreenUpdating=Falseだけで十分では?

小規模データ(数百行)なら十分。ただし1万行以上では配列の効果が圧倒的。ScreenUpdating=Falseは×2.5の高速化だが、配列は×25。10倍以上の差がある。大量データを扱うなら配列を使うべき。

Q3: 配列が使えないケースはある?

セルの書式(背景色・フォント・罫線など)を変更する処理は配列では扱えない。配列で扱えるのはセルの「値」のみ。書式変更が必要な場合はセルアクセスが必要だが、ScreenUpdating=Falseは併用できる。行の色分け処理(/006)ではScreenUpdating=Falseが有効。

Q4: 処理時間を計測する方法は?

Timer関数を使う。テクニック3のテンプレートに処理時間の計測が組み込まれている。実行すると「完了(0.23秒)」のようにMsgBoxで表示される。


Dim startTime As Double
startTime = Timer
' 処理
MsgBox Format(Timer - startTime, "0.00") & "秒"

Q5: 10万行以上のデータでも配列で処理できる?

可能。Variant配列のメモリ使用量は概算で行数×列数×16バイト程度。10万行×10列でも約15MB。一般的なPCのメモリ(8GB以上)なら問題ない。ただし100万行を超える場合は分割処理を検討する。処理の進捗表示が必要な場合は /026 を参照。

---

まとめ

この記事では、VBAの処理速度を10倍以上に改善する3つのテクニックを解説した。

  • テクニック1:ScreenUpdating=False + Calculation=xlManual → 約2.5倍速。1行追加するだけ
  • テクニック2:配列で一括読み書き → 約25倍速。最も効果が大きい
  • テクニック3:全テクニック併用テンプレート → 約40倍速。エラー時の設定復帰付き

大量データを扱うマクロでは、テクニック3のテンプレートをそのまま使うのがおすすめ。

関連記事:

  • /005 複数Excelファイルの統合 — 配列と組み合わせると統合処理も高速化できる
  • /015 複数シートの一括処理 — シートループ×配列で大幅に速くなる
  • /026 進捗表示 — 長時間処理の進捗バーを表示する方法
  • /032 最終行の取得 — 配列の範囲指定に必須のテクニック

次にやりたくなること:

  • /032 最終行の正確な取得方法を知りたい — 配列の範囲指定に必須のテクニック
  • /026 長時間処理の進捗バーを表示したい — 配列処理でも進捗が見えると安心
  • /005 複数Excelファイルの統合を高速化したい — 配列と組み合わせて統合処理も一気に速くなる
  • /022 エラー処理で止まらないマクロにしたい — 配列処理中のエラーでScreenUpdatingが戻らない問題を防げる
  • /015 全シートに配列処理を一括適用したい — シートループ×配列で大量シートの処理を劇的に高速化

---

もっとカスタマイズしたい場合

「既存のマクロを配列に書き換えて高速化したい」「大量データの処理を自動化したい」「10万行以上のデータを分割処理したい」など、業務に合わせたカスタマイズが必要な場合は、ココナラで相談できます。

相談時に以下の情報があるとスムーズです:

  • Excel のバージョン / OS
  • 現在のマクロの処理内容(何をしているか)
  • データの行数・列数(目安)
  • 現在の処理時間と目標時間

コメント

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