
目次
- はじめに
- 検証用データの準備
- 検証条件と比較対象
1. 行ループ(条件に合う行を1行ずつ確認してコピペする)
2. AutoFilter(Excelのフィルター機能をVBAで呼び出し、抽出してからコピペする)
3. 配列ループ(元データを配列に格納してメモリ内でループ処理してから書き出す) - 検証結果
- おわりに
はじめに
業務でVBAを使用する際には、概して膨大なデータを扱います。
特に「膨大なCSVデータを処理する際、どのような方法で処理を行うのが最も早いのか?」というのを今回のテーマとします。
検証用データの準備
まず、検証用データを作成します。とはいっても今回のテーマは「膨大なデータ」です。
数万件のデータを手打ちで準備するのは現実的ではないので、これもVBAで作成してしまうことにしました。
今回は、下記のように「ID(通番)」「名前」「地域」「売上」の項目がすべて埋められた10万行+見出しのデータを用意します。
それぞれ1行ずつ作成するマクロでも良いのですが、処理に時間がかかるので、配列を使用します。
Sub CreateDummyData()
Dim startTime As Double
startTime = Timer
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim ws As Worksheet
Set ws = ActiveSheet
ws.Cells.Clear ' シートを初期化
ws.Range("A1:D1").Value = Array("ID", "名前", "地域", "売上")
Dim maxRows As Long
maxRows = 100000
Dim dataArray() As Variant
ReDim dataArray(1 To maxRows, 1 To 4)
Dim regions As Variant
regions = Array("東京", "大阪", "名古屋", "福岡", "札幌")
Dim i As Long
Randomize
For i = 1 To maxRows
dataArray(i, 1) = i ' ID
dataArray(i, 2) = "ユーザー_" & i ' 名前
dataArray(i, 3) = regions(Int(Rnd() * 5)) ' 地域 (ランダム)
dataArray(i, 4) = Int((Rnd() * 9000) + 1000) ' 売上 (1000~10000のランダム)
Next i
ws.Range("A2").Resize(maxRows, 4).Value = dataArray
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
MsgBox "ダミーデータ作成が完了しました。" & vbCrLf & _
"処理時間: " & Format(Timer - startTime, "0.00秒"), vbInformation
End Sub
上記のようなコードとなりました。
早速、Excel上で実行してみます。


このような結果となりました。
スクリーンショット上には写っていませんが、きちんと10万件のデータが作成されています。
今回は、このデータを使って処理速度の検証を行うことにしましょう。
今回は、それぞれ検証のため、検証結果_RowLoop、検証結果_Filter、検証結果_Arrayという名前の空白シートを準備してください。※下記のコードは、シートが作成されていないとエラーとなります。
パターン1. 「行ループ」のコード
Sub Test_RowLoop()
Dim startTime As Double
startTime = Timer
Dim wsSource As Worksheet: Set wsSource = Sheets(1)
Dim wsDest As Worksheet: Set wsDest = Sheets("検証結果_RowLoop")
wsDest.Cells.Clear
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' ヘッダーのコピー
wsSource.Range("A1:D1").Copy wsDest.Range("A1")
Dim lastRow As Long
lastRow = wsSource.Cells(wsSource.Rows.Count, "A").End(xlUp).Row
Dim i As Long, destRow As Long
destRow = 2
' 1行ずつセルを確認してコピペ
For i = 2 To lastRow
If wsSource.Cells(i, 3).Value = "東京" Then
wsSource.Range(wsSource.Cells(i, 1), wsSource.Cells(i, 4)).Copy wsDest.Cells(destRow, 1)
destRow = destRow + 1
End If
Next i
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
MsgBox "【通常の行ループ】処理完了" & vbCrLf & _
"処理時間: " & Format(Timer - startTime, "0.000秒"), vbInformation
End Sub
パターン2. 「AutoFilter」のコード
Sub Test_AutoFilter()
Dim startTime As Double
startTime = Timer
Dim wsSource As Worksheet: Set wsSource = Sheets(1) ' ダミーデータシート
Dim wsDest As Worksheet: Set wsDest = Sheets("検証結果_Filter")
' 出力先シートの初期化
wsDest.Cells.Clear
' 高速化設定
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' オートフィルターを実行して「東京」を抽出
wsSource.Range("A1").AutoFilter Field:=3, Criteria1:="東京"
' 可視セル(抽出された行)のみをコピーして貼り付け
wsSource.UsedRange.SpecialCells(xlCellTypeVisible).Copy wsDest.Range("A1")
' フィルターを解除
wsSource.AutoFilterMode = False
' 設定を元に戻す
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
MsgBox "【AutoFilter】処理完了" & vbCrLf & _
"処理時間: " & Format(Timer - startTime, "0.000秒"), vbInformation
End Sub
パターン3. 「配列ループ」のコード
Sub Test_ArrayLoop()
Dim startTime As Double
startTime = Timer
Dim wsSource As Worksheet: Set wsSource = Sheets(1)
Dim wsDest As Worksheet: Set wsDest = Sheets("検証結果_Array")
wsDest.Cells.Clear
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim lastRow As Long
lastRow = wsSource.Cells(wsSource.Rows.Count, "A").End(xlUp).Row
Dim sourceArray As Variant
sourceArray = wsSource.Range("A1:D" & lastRow).Value
Dim destArray() As Variant
ReDim destArray(1 To lastRow, 1 To 4)
' ヘッダーを格納
Dim col As Long
For col = 1 To 4
destArray(1, col) = sourceArray(1, col)
Next col
' ループ処理
Dim i As Long, destRow As Long
destRow = 2 ' 2行目から格納開始
For i = 2 To lastRow
If sourceArray(i, 3) = "東京" Then
destArray(destRow, 1) = sourceArray(i, 1) ' ID
destArray(destRow, 2) = sourceArray(i, 2) ' 名前
destArray(destRow, 3) = sourceArray(i, 3) ' 地域
destArray(destRow, 4) = sourceArray(i, 4) ' 売上
destRow = destRow + 1
End If
Next i
' シートへ書き出し
If destRow > 2 Then
wsDest.Range("A1").Resize(destRow - 1, 4).Value = destArray
End If
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
MsgBox "【配列ループ】処理完了" & vbCrLf & _
"処理時間: " & Format(Timer - startTime, "0.000秒"), vbInformation
End Sub
検証結果
パターン1. 「行ループ」の検証結果
行ループでの検証結果は下記の通りの結果になりました。
予想していたことではありますが、1行ずつの処理は非効率で、とても時間がかかっています。
出力はしっかりできていましたが、検証データよりもっと膨大なデータを扱う実務環境では現実的な方法ではないことがわかります。


パターン2. 「AutoFilter」での検証結果
やはり、行ループに比べ、圧倒的に早かったです。これなら実務でも問題ありません。
※結果は行ループと同じだったのでスクリーンショットは処理時間のみとします。

パターン3. 「配列ループ」での検証結果
さて、次に配列ループですが、AutoFilterを超える速度で処理完了となりました。

検証結果のまとめ
「行ループ」の場合は、10万回シートを見に行き、10万回シートに書き込むというやり取りのため、単純に時間がかかっています。人間に例えるなら、1行1行指し示し目視で確認しながら書き写しているようなものです。
それと比較して、AutoFilterはExcel側のフィルター機能を利用するので、十分に早いと言えますし、配列に関しては、Excel側とのやり取りが読み込みと書き出しのみなので、圧倒的に早くなります。
大量のデータ処理は、いかにExcel側とのやり取りを減らすかが重要だと考えられますね。
おわりに
今回は検証ダミーデータの作成から、速度検証までを実際に行ってみました。
特にExcelや、Excelで加工したいCSVのデータは膨大になりがちなので、処理速度については重要なテーマだと考えています。
AutoFilterの手軽さを取るか、配列の堅さを取るかは、それぞれの業務によって異なると思いますが、都度できるだけ最適な選択を目指したいですね。
