wrote :: 2005.01.18

トップページ > Excel > VBA > 高速化テクニック > Selectするな!


Selectするな!

セル範囲A1:A5に数字を入力する操作をマクロ記録すると、次のようなコードが生成されます。

Sub Macro1()
    Range("A1").Select
    ActiveCell.FormulaR1C1 = "1"
    Range("A2").Select
    ActiveCell.FormulaR1C1 = "2"
    Range("A3").Select
    ActiveCell.FormulaR1C1 = "3"
    Range("A4").Select
    ActiveCell.FormulaR1C1 = "4"
    Range("A5").Select
    ActiveCell.FormulaR1C1 = "5"
    Range("A6").Select
End Sub

それぞれのセルを選択(Select)し、そのアクティブセルに対して数値を入力したのですから、このコードは間違いではありません。ただし、決して効率の良いコードとも呼べません。

マクロ記録は、操作を1ステップずつコード化します。つまり「セルA1に1と入力する」操作は、まず「セルA1が選択された」「選択されたセルに1が入力された」さらにEnterキーを押すと「アクティブセルが1つ下に下がった」というように記録されるのです。このようなコードは速度的に不利なだけでなく、メンテナンス性も大きく損なわれます。

 Test1Test2%
1回目00:1400:1285.7%
2回目00:1400:1285.7%
3回目00:1400:1285.7%
4回目00:1500:1280.0%
5回目00:1500:1386.7%
6回目00:1500:1280.0%
7回目00:1400:1285.7%
8回目00:1400:1285.7%
9回目00:1400:1285.7%
10回目00:1400:1178.6%
平均00:1400:1283.9%
左の検証は、下のTest1とTest2の結果です。平均して2割弱の高速化でしょうか。

昔Excel 95あたりで試したときはもっと差が出たのですが、最近のパソコンは高性能ですから、Selectしていてもそこそこの速度が出るのでしょう。
Selectしない方がいいのは速度的な理由だけではありません。コードの行数が少なくなることで、マクロ全体の可読性が高まるからです。可読性が高まればバグも減るでしょう。それに何より、Selectの連呼は美しくありません(^^;

Sub Test1()
    Dim i As Integer, j As Integer
    For i = 1 To 100
        For j = 1 To 10
            Cells(j, 1).Select
            Selection.Value = j
        Next j
    Next i
End Sub

Sub Test2()
    Dim i As Integer, j As Integer
    For i = 1 To 100
        For j = 1 To 10
            Cells(j, 1).Value = j
        Next j
    Next i
End Sub

Selectメソッドとよく似た働きをするものにActivateメソッドがあります。選択するSelectと、アクティブにするActivateは、Excel上でほとんど同じ動作ですので、高速化のためになるべくActivateしないように…という欲も出てきます。しかし、中にはActivateしないでオブジェクトを直接操作しようとするとエラーになることもありますから注意が必要です。それは、たとえばグラフなどのオブジェクトです。

次のコードは、グラフの背景色を赤色に変更した操作をマクロ記録しました。本当はもう少し他のコードも記録されるのですが、ここでは割愛しました。

Sub Macro1()
    ActiveSheet.ChartObjects("グラフ 1").Activate
    ActiveChart.ChartArea.Select
    With Selection.Interior
        .ColorIndex = 3
        .PatternColorIndex = 1
        .Pattern = 1
    End With
    Windows("Book1.xls").Activate
    Range("A1").Select
End Sub

このコードで高速化できそうな部分は「Activate」「Select」「Selection」あたりです。
最後の「Range("A1").Select」は、グラフの選択を解除する操作です。その直前の「Widnows("Book1.xls").Activate」も似た動作ですから、とりあえず「Widnows("Book1.xls").Activate」はコメントしときましょう。また、グラフをアクティブにしなければ、選択を解除する必要もありませんから「Range("A1").Select」もコメントにしときます。
さて、問題は先頭部分です。まずはSelectionからいきましょう。「ActiveChart.ChartArea.Select」と「With Selection.Interior」を合体させて「With ActiveChart.ChartArea.Interior」に書き換えます。実行してみると、最後にグラフの選択状態が解除されませんが、とりあえずうまくいきます。
「ActiveSheet.ChartObjects("グラフ 1")」と「ActiveChart」は同じオブジェクトですから、これをまとめればActivateメソッドがなくなります。ここまでの修正で次のようなコードになります。

Sub Macro1()
    With ActiveSheet.ChartObjects("グラフ 1").ChartArea.Interior
        .ColorIndex = 3
        .PatternColorIndex = 1
        .Pattern = 1
    End With
    ''Windows("Book1.xls").Activate
    ''Range("A1").Select
End Sub

しかし、このコードはエラーになります。ChartAreaオブジェクトはActiveChartでないと操作できないのです。つまり、どうしてもグラフをActivateしなければならないということです。しかたありませんので最初の「ActiveSheet.ChartObjects("グラフ 1").Activate」は残します。グラフのActivateを避けられないのですから、最後の「Range("A1").Select」も必要です。その代わり、背景を赤色にするColorIndexプロパティ以外を削除し、一瞬グラフが選択されるのを隠すためScreenUpdatingプロパティにFalseを設定します。

Sub Macro1()
    Application.ScreenUpdating = False
    ActiveSheet.ChartObjects("グラフ 1").Activate
    ActiveChart.ChartArea.Interior.ColorIndex = 3
    Range("A1").Select
    Application.ScreenUpdating = True
End Sub


[表示を止めろ!]戻る← | →進む[余計なことはするな!]