【VBA】高速化

そもそもの話

年末年始に引きこもる準備で忙しいjimです。どうも✋

今回は、前回に引き続き『高速化』についてです。

そもそもの話、何で描画やらイベントマクロの停止なんてことをしないといけないかっていうと、Excelを使うからじゃね?Cellを使うからじゃね?っていうね。っていう話よ。

先ず、Cellを使った処理って遅いのね。だから、使えば使うほど処理時間って長くなるわけ。。。

で、殆どの仕事っていうのは過程なんてものはどうでもよくて、最終的に成果物があればよかったりするわけじゃん?

うん。要するに、VBA使えるなら無理してExcel使う必要ないんじゃね?ってこと。

メンテナンス?知らん。個人で作ったマクロなんて、使えなくなったらポイでいいでしょ。

最悪、手動でも効率よくできるようにしておいて、そのマニュアルを残しといたらいい。また誰かが自動化してくれるさ。

 

配列を使う

例えば、Excelのセル同士で何らかの計算をして、その結果を新しいブックにエクスポート。なんてこと、、、よくありますよね?

以下のコードは新しいブックを開いて、単純に適当な文字列を入力しcsvファイルとして保存するものです。ただし10万行。

Option Explicit
Sub a()
Dim myTime1 As Double
Dim myTime2 As Double
Dim newBook As Workbook
Dim newSheet As Worksheet
Dim i As Long
    Call ExcelEvents(False)
    myTime1 = Timer
    Set newBook = Workbooks.Add
    Set newSheet = newBook.Worksheets(1)
    With newSheet
        .Cells(1, 1) = "No"
        .Cells(1, 2) = "No+1"
        .Cells(1, 3) = "No+2"
        For i = 1 To 100000
            .Cells(i + 1, 1) = i
            .Cells(i + 1, 2) = i + 1
            .Cells(i + 1, 2) = i + 2
        Next
    End With
    newBook.SaveAs fileName:=ThisWorkbook.Path & "\テスト.csv", FileFormat:=xlCSV
    newBook.Close savechanges:=False
    myTime2 = Timer
    Call ExcelEvents(True)
    MsgBox myTime2 - myTime1
End Sub
Private Sub ExcelEvents(ByVal myBoolean As Boolean)
    With Application
        .ScreenUpdating = myBoolean '描画停止
        .EnableEvents = myBoolean 'イベントマクロ停止
        If myBoolean = False Then
            .Calculation = xlCalculationManual '手動計算
            .Cursor = xlWait '砂時計
        Else
            .Calculation = xlCalculationAutomatic '自動計算
            .Cursor = xlDefault 'デフォ矢印
        End If
    End With
End Sub

Excelのイベント類は全て停止させているのにも関わらず。。。

結果は10秒以上かかります。

次は配列を使います。一旦、配列に10万行分の文字列を格納します。その後、一括で配列をシートにエクスポートしてやります。

Option Explicit
Sub b()
Dim myTime1 As Double
Dim myTime2 As Double
Dim newBook As Workbook
Dim newSheet As Worksheet
Dim i As Long
Dim myArr(0 To 100000, 0 To 2) As String
    Call ExcelEvents(False)
    myTime1 = Timer
    Set newBook = Workbooks.Add
    Set newSheet = newBook.Worksheets(1)
    myArr(0, 0) = "No"
    myArr(0, 1) = "No+1"
    myArr(0, 2) = "No+2"
    For i = 1 To 100000
        myArr(i, 0) = i
        myArr(i, 1) = i + 1
        myArr(i, 2) = i + 2
    Next
    newSheet.Range("A1:C" & UBound(myArr, 1) + 1) = myArr()
    newBook.SaveAs fileName:=ThisWorkbook.Path & "\テスト.csv", FileFormat:=xlCSV
    newBook.Close savechanges:=False
    myTime2 = Timer
    Call ExcelEvents(True)
    MsgBox myTime2 - myTime1
End Sub
Private Sub ExcelEvents(ByVal myBoolean As Boolean)
    With Application
        .ScreenUpdating = myBoolean '描画停止
        .EnableEvents = myBoolean 'イベントマクロ停止
        If myBoolean = False Then
            .Calculation = xlCalculationManual '手動計算
            .Cursor = xlWait '砂時計
        Else
            .Calculation = xlCalculationAutomatic '自動計算
            .Cursor = xlDefault 'デフォ矢印
        End If
    End With
End Sub

配列を使っただけなのに、かなり高速化できました。

あまりにも処理時間が長いものは、イベント類を停止させるよりも先に見直さないといけないことがあるかも?

 

アプリケーションを使わない

そもそもExcelを使わないようにした方が、ダンチのレベチで高速化できる。

Sub c()
Dim myTime1 As Double
Dim myTime2 As Double
Dim csvStr As String
Dim i As Long
    myTime1 = Timer
    Open ThisWorkbook.Path & "\テスト.csv" For Output As #1
    Print #1, "No,No+1,No+2";
    For i = 1 To 100000
        Print #1, vbCrLf & i;
        Print #1, "," & i + 1;
        Print #1, "," & i + 2;
    Next
    Close #1
    myTime2 = Timer
    MsgBox myTime2 - myTime1
End Sub

Excelのイベント類停止なんて意味がないので不要。

 

 

はい、ってことで高速化についてでした。

VBAプログラム組めるなら、Excel使わなくてもいいことが沢山あると思います。

どうしてもExcelでないとダメ。ってもの以外は、なるべくVBAだけで処理してみては如何でしょうか?

したら、またねぇ✋