web-dev-qa-db-ja.com

VBA(Excel)の行をループする最も効率的で迅速な方法は何ですか?

ExcelのVBAは最速ではありませんが、行の大きなサンプルをループする最も効率的な(つまり、最も速い)方法が必要です。

現在私が持っているもの:

For Each c In Range("$A$2:$A$" & Cells(Rows.count, "A").End(xlUp).row
    ' do stuff
Next c

'do stuff'には、あちこちに行を挿入することが含まれます(そのため、範囲の動的ルックアップを維持する必要があります)。

アイデア(10,000行以上)

編集私はすでに使用しています

Application.ScreenUpdating = False
Application.Calculation = xlManual
23
Chris

列Aの1万行をループするだけの場合は、その行をバリアント配列にダンプしてからループします。

次に、必要に応じて行を追加しながら新しい配列に要素を追加し、Transpose()を使用して配列を1回の移動で範囲に配置するか、反復子変数を使用してどの行にいるかを追跡し、追加できますそのように行。

Dim i As Long
Dim varray As Variant

varray = Range("A2:A" & Cells(Rows.Count, "A").End(xlUp).Row).Value

For i = 1 To UBound(varray, 1)
    ' do stuff to varray(i, 1)
Next

各セルを評価した後に行を追加する方法の例を次に示します。この例では、列Aに単語「foo」を持つすべての行の後に行を挿入します。A2から開始しているため、挿入中に変数iに「+2」が追加されるわけではありません。 A1で配列を開始した場合、+ 1になります。

Sub test()

Dim varray As Variant
Dim i As Long

varray = Range("A2:A10").Value

'must step back or it'll be infinite loop
For i = UBound(varray, 1) To LBound(varray, 1) Step -1
    'do your logic and evaluation here
    If varray(i, 1) = "foo" Then
       'not how to offset the i variable 
       Range("A" & i + 2).EntireRow.Insert
    End If
Next

End Sub
36
aevanko

[〜#〜] edit [〜#〜]要約と推奨事項

を使って for each cell in range構文自体は自体ではありませんis遅いのは、ループ内でExcelに繰り返しアクセスすることです(セル値の読み取りまたは書き込み、フォーマットなど、行の挿入/削除など)。

遅すぎるものは、ニーズによって大きく異なります。まれにしか使用されない場合、実行に数分かかるSubは問題ないかもしれませんが、10秒かかるサブは頻繁に実行されると遅すぎるかもしれません。

したがって、いくつかの一般的なアドバイス:

  1. 最初はシンプルにしてください。結果がニーズに対して遅すぎる場合は、最適化する
  2. ループの内容の最適化に焦点を当てる
  3. ループが必要であると単に仮定しないでください。いつか別の方法があります
  4. ループ内で(多くの)セル値を使用する必要がある場合は、それらをループ外のバリアント配列にロードします。
  5. 挿入の複雑さを回避する良い方法は、範囲をボトムアップでループすることです
    for index = max to min step -1
  6. それができず、「ここに行を挿入」が多すぎない場合は、各挿入後に配列を再読み込みすることを検討してください
  7. value以外のセルプロパティにアクセスする必要がある場合、セル参照にこだわる
  8. 多数の行を削除するには、ループ内のマルチエリア範囲への範囲参照を構築し、ループ後一度にその範囲を削除することを検討してください

例(テストされていません!)

Dim rngToDelete as range
for each rw in rng.rows
    if need to delete rw then

        if rngToDelete is nothing then
            set rngToDelete = rw
        else
            set rngToDelete = Union(rngToDelete, rw)
        end if

    endif
next
rngToDelete.EntireRow.Delete

元の投稿

セルをループすることはbadであり、バリアント配列をループすることはgood。私もこれを長年擁護してきました。あなたの質問に考えさせられたので、私は驚くべき(とにかく)結果でいくつかの短いテストを行いました:

テストデータセット:セル内の単純なリストA1 .. A1000000(1,000,000行)

テストケース1:配列をループする

Dim v As Variant
Dim n As Long

T1 = GetTickCount
Set r = Range("$A$1", Cells(Rows.Count, "A").End(xlUp)).Cells
v = r
For n = LBound(v, 1) To UBound(v, 1)
    'i = i + 1
    'i = r.Cells(n, 1).Value 'i + 1
Next
Debug.Print "Array Time = " & (GetTickCount - T1) / 1000#
Debug.Print "Array Count = " & Format(n, "#,###")

結果:

Array Time = 0.249 sec
Array Count = 1,000,001

テストケース2:範囲をループする

T1 = GetTickCount
Set r = Range("$A$1", Cells(Rows.Count, "A").End(xlUp)).Cells
For Each c In r
Next c
Debug.Print "Range Time = " & (GetTickCount - T1) / 1000#
Debug.Print "Range Count = " & Format(r.Cells.Count, "#,###")

結果:

Range Time = 0.296 sec
Range Count = 1,000,000

したがって、配列のループより高速ですが、19%だけ-予想よりはるかに少ないです。

テスト3:セル参照で配列をループする

T1 = GetTickCount
Set r = Range("$A$1", Cells(Rows.Count, "A").End(xlUp)).Cells
v = r
For n = LBound(v, 1) To UBound(v, 1)
    i = r.Cells(n, 1).Value
Next
Debug.Print "Array Time = " & (GetTickCount - T1) / 1000# & " sec"
Debug.Print "Array Count = " & Format(i, "#,###")

結果:

Array Time = 5.897 sec
Array Count = 1,000,000

テストケース4:セル参照を含むループ範囲

T1 = GetTickCount
Set r = Range("$A$1", Cells(Rows.Count, "A").End(xlUp)).Cells
For Each c In r
    i = c.Value
Next c
Debug.Print "Range Time = " & (GetTickCount - T1) / 1000# & " sec"
Debug.Print "Range Count = " & Format(r.Cells.Count, "#,###")

結果:

Range Time = 2.356 sec
Range Count = 1,000,000

したがって、単一の単純なセル参照を使用したイベントでは、ループは1桁遅くなり、さらに、範囲ループは2倍高速になります。

したがって、結論は最も重要なのはループ内での処理であり、速度が本当に重要な場合はすべてのオプションをテストします

FWIW、Excel 2010 32ビット、Win7 64ビットでテスト済み

  • ScreenUpdating off、
  • Calulationマニュアル、
  • Eventsは無効です。
19
chris neilsen

For Eachは、何らかの理由でI = 1からXよりもはるかに高速です。同じ辞書を調べてみてください。


dDictのDkeyごとに1回、


dkey = lbound(dDict.keys)からubound(dDict.keys)まで

=>同じコンストラクトを使用している場合でも、大きな違いに気付くでしょう。

1
Dumitru Daniel