在VBA(excel)中循环遍历行的最有效/最快捷的方法是什么?

Chr*_*ris 23 excel vba loops excel-vba

我知道Excel中的VBA并不是最快的东西 - 但我需要最有效(即最快)的方法来循环遍历大量的行.

目前我有:

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

'do stuff'包括在这里和那里插入一行(所以我需要保持范围的动态查找.)

任何想法(看10,000行+)?

编辑我已经在使用了

Application.ScreenUpdating = False
Application.Calculation = xlManual
Run Code Online (Sandbox Code Playgroud)

aev*_*nko 37

如果您只是循环遍历A列中的10k行,则将该行转储到变量数组中,然后循环遍历该行.

然后,您可以将元素添加到新数组(在需要时添加行),并使用Transpose()将数组一次性放入您的范围,或者您可以使用迭代器变量来跟踪您所在的行并添加这样的行.

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
Run Code Online (Sandbox Code Playgroud)

以下是在评估每个单元格后如何添加行的示例.此示例仅在A列中具有单词"foo"的每一行之后插入一行.由于我们在A2上启动,因此在插入期间不会将"+2"添加到变量i.如果我们用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
Run Code Online (Sandbox Code Playgroud)

  • 有时不需要+1循环.将范围转储到数组中,执行数组所需的操作,然后将其转储回来.当您需要向原始数据"添加行"时,动态第二个数组(使用Redim)可以提供帮助. (4认同)

chr*_*sen 19

编辑 摘要和推荐

使用for each cell in range构造本身并不慢.什么缓慢的是在循环中重复访问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
Run Code Online (Sandbox Code Playgroud)

原帖

传统观点认为循环通过细胞是不好的,并且通过变体阵列循环是好的.一段时间以来,我也一直是这方面的倡导者.你的问题让我思考,所以我做了一些简短的测试,惊讶(对我来说)结果:

测试数据集:单元格中的简单列表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, "#,###")
Run Code Online (Sandbox Code Playgroud)

结果:

Array Time = 0.249 sec
Array Count = 1,000,001
Run Code Online (Sandbox Code Playgroud)

测试案例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, "#,###")
Run Code Online (Sandbox Code Playgroud)

结果:

Range Time = 0.296 sec
Range Count = 1,000,000
Run Code Online (Sandbox Code Playgroud)

因此,循环数组速度更快,但只有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, "#,###")
Run Code Online (Sandbox Code Playgroud)

结果:

Array Time = 5.897 sec
Array Count = 1,000,000
Run Code Online (Sandbox Code Playgroud)

测试用例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, "#,###")
Run Code Online (Sandbox Code Playgroud)

结果:

Range Time = 2.356 sec
Range Count = 1,000,000
Run Code Online (Sandbox Code Playgroud)

因此,使用单个简单单元格引用的事件,循环慢一个数量级,而且更多,范围循环的速度是其两倍!

所以,结论最重要的是你在循环中事情,如果速度真的很重要,那么测试所有的选项

FWIW,在Excel 2010 32位,Win7 64位上测试所有测试

  • ScreenUpdating 关,
  • Calulation 手册,
  • Events 禁用.

  • 你错过了最重要的测试 - 用varray参考循环varray.测试3没有意义 - 为什么循环一个数组但是为每个元素调用excel并忽略数组的内容?我认为你会发现循环数组并且引用它的元素比循环一个范围和引用单元格要快得多(确保你测试你正在测试的单元格内的数据(而不是空单元格)). (2认同)
  • 与通过`r.Cells(n,1)`中的`n`循环相比,"范围循环速度快两倍"的原因在于,与For Up相比,`For Each`结构在进行集合游戏方面本质上更快收集成员通过他们的索引,这是较慢的.当然,如果你决定将后者称为"循环变体",那么"循环变体"就不好了,但这不是它的本质; 它是通过他们的索引"循环[一系列]细胞",传统智慧准确地描述为慢. (2认同)