Excel 每个环路的VBA为64K+;列表行(内存ouf)

Excel 每个环路的VBA为64K+;列表行(内存ouf),excel,vba,foreach,Excel,Vba,Foreach,我正在通过Excel表(Listobject)为每个循环运行VBA,该表根据给定路径检查文件是否存在。但我的表已经扩展,有68K行列表。启动代码后,它会很快给出一个错误运行时错误“7”:内存不足 它可以运行63K行(在5分钟内完成),根据谷歌搜索,似乎有一个叫做“64K段边界”的东西。这就是影响我的代码运行的原因,因为它确实感觉到它在开始时缓冲了行数,然后在没有开始实际运行任何东西的情况下反弹回来。是否有一种简单的解决方法,无需将数据集拆分为多个批次?坦率地说,我很惊讶2021年64K限制仍然是

我正在通过Excel表(Listobject)为每个循环运行VBA
,该表根据给定路径检查文件是否存在。但我的表已经扩展,有68K行列表。启动代码后,它会很快给出一个错误
运行时错误“7”:内存不足

它可以运行63K行(在5分钟内完成),根据谷歌搜索,似乎有一个叫做“64K段边界”的东西。这就是影响我的代码运行的原因,因为它确实感觉到它在开始时缓冲了行数,然后在没有开始实际运行任何东西的情况下反弹回来。是否有一种简单的解决方法,无需将数据集拆分为多个批次?坦率地说,我很惊讶2021年64K限制仍然是Excel中的一个东西

在64位Excel 2019上运行它,但在Office365上也不走运

Sub CheckFiles()

Dim Headers As ListObject
Dim lstrw As ListRow

Dim strFileName As String
Dim strFileExists As String

Application.ScreenUpdating = False

Set ws = ThisWorkbook.Sheets("Import")
Set Headers = ws.ListObjects("Import")

    For Each lstrw In Headers.ListRows
    
        strFileName = lstrw.Range(7)
        strFileExists = Dir(strFileName)
        
        If strFileExists = "" Then
        lstrw.Range(4) = "not found"
        Else
        lstrw.Range(4) = "exists"
        End If
    
    Next lstrw

Set ws = Nothing
Set Headers = Nothing

Application.ScreenUpdating = True

End Sub
避免访问工作表
  • 由于无法避免循环,因此最好在计算机内存中进行循环,也就是说,通过数组的元素,而不是通过范围的单元格
  • 代码仍然很慢,对于我的机器上的200000行,大约10秒,但这是因为
    Dir
  • 请注意,将范围写入(复制)数组(
    Data=rg.Value
    )并将数组写入(复制)范围(
    rg.Value=Data
    )是多么容易(当范围包含多个单元格时,仅一行)和多么快(一秒钟)
  • 调整常量部分中的值
选项显式
子检查文件()
Const wsName As String=“Import”'工作表名称
Const tblName As String=“Import”表名
Const cCol长度=7'标准列
Const dCol长度=4'目标列
将wb设置为工作簿:设置wb=ThisWorkbook
将ws设置为工作表:设置ws=wb.Worksheets(wsName)
将标题设置为ListObject:Set Headers=ws.ListObjects(tblName)
Dim数据作为变量的数据数组
带有Headers.ListColumns(cCol.DataBodyRange)
如果.Rows.Count=1,则
ReDim数据(1对1,1对1):数据=.Value
其他的
数据=.Value
如果结束
以
Dim r As Long'数组行计数器(数据源范围行计数器)
Dim FileName作为字符串“Dir检索到的文件名”
对于r=1到uBond(数据,1)
文件名=目录(CStr(数据(r,1)))
如果Len(FileName)=0,则
数据(r,1)=“未找到”
其他的
数据(r,1)=“存在”
如果结束
下一个r
Headers.ListColumns(dCol).DataBodyRange.Value=Data
端接头

谢谢大家!一些外卖。显然,尽管我们试图编写尽可能高效的代码,但这里任何合理的性能都是可以接受的。话虽如此,
对于每个
循环,用63K行运行大约需要5分钟,而我接受@vbasic208作为答案的代码在大约15秒钟内就完成了这项工作-也没有容量问题

我对这段代码的唯一问题是,它对我来说是一种新的方法,因此将来可能需要在它的基础上进行更深入的研究——但它看起来确实很有效。我还为。。。到
循环,该循环也不会遇到68K行的问题,并会使用
偏移功能在行和列之间切换

对于每个as@p,明显快于
ᴇʜ建议,但所需时间约为数组方法的2倍(30秒左右)


将所需数据读入数组,使用循环处理数组,然后将结果写入数组中的单元格。使用阵列时,这应该运行得更快。每个读写操作都会带来大量开销。如果使用数组,则在开始时只有一个读取操作,在结束时只有一个写入操作。如果使用常规范围而不是listobject,您会看到相同的情况吗?只是做了一个测试:可以确认该行为。更改为常规的
for
-循环(
for i=1 to list.ListRows.Count
)可以毫无问题地工作,在3秒内迭代70k行。@FunThomas现在这很有趣,因为通常情况下,对象集合上的每个…下一个
比…下一个
循环的
快几个数量级。Excel
ListObject
API中是否存在内存泄漏错误?
For Each…Next
使用枚举器机制一次生成一个对象。。。看起来像是
ListRows
集合中的一个实现错误ᴇʜ的评论解释了为什么使用数组更快:范围读取一次,写入一次,因此工作表只被访问两次(当然,数据更多),而在一个范围中循环读取和写入每行的数据一次(例如,访问工作表13万次)。当使用对象并使用
For…Next
循环时,可能总是可以使用
For Each…Next
循环来编写代码,这应该更快。明天我会在我的答案中添加这样一个解决方案,这样你就可以自己比较了。
Sub CheckFiles_2()

Dim strFileName, strFileExists As String
Dim ws As Worksheet

Dim Headers As ListObject
Dim result As String
Dim counter, RowCount As Long

Application.ScreenUpdating = False

Set ws = ThisWorkbook.Sheets("Import")
Set Headers = ws.ListObjects("Import")

RowCount = Headers.ListRows.Count

For counter = 1 To RowCount

strFileName = Range("anchorCell").Offset(counter, 3)

        strFileExists = Dir(strFileName)
        
        If strFileExists = "" Then
        result = "not found"
        Else
        result = "exists"
        End If

Range("anchorCell").Offset(counter, 0) = result

Next counter

Set ws = Nothing
Set Headers = Nothing

Application.ScreenUpdating = True

End Sub