“Excel上的VBA”;“内存不足”;错误
我在Excel 2010上,在一张公认的非常大的表上(400k行X 20列) 我的守则旨在:“Excel上的VBA”;“内存不足”;错误,vba,excel,Vba,Excel,我在Excel 2010上,在一张公认的非常大的表上(400k行X 20列) 我的守则旨在: 将整个工作表加载到数组中 检查每一行的特定条件 符合条件的行将复制到另一个数组中 最后,将第二个数组返回到另一个工作表 第二个阵列最终将大约是原始阵列的90% 我将两个变量数组定义为变量 并试图通过复制工作表的内容两次来初始化它们 第一个副本有效,但到第二个副本时,我遇到了“内存不足”的错误 有解决办法吗?或者这只是VBA/Excel的一个限制 有没有办法不预先定义/初始化目标阵列,而是让它随着标准
- 将整个工作表加载到数组中
- 检查每一行的特定条件
- 符合条件的行将复制到另一个数组中
- 最后,将第二个数组返回到另一个工作表
- 第二个阵列最终将大约是原始阵列的90%
正如你帖子上的评论所指出的,这个错误来自于工作记忆的不足 每个变量类型变量消耗16个字节,这就是代码需要大量内存的原因。所以解决这个问题的一个方法是增加计算机上的物理内存 另一种解决方案是按一定数量的行过滤数据
Sub ProcessRows()
Dim originalData() As Variant
Dim maxRow as Long, currentRow as Long, incrementRow
maxRow = ActiveSheet.Usedrange.Rows.Count
currentRow =1
incrementRow=5000
While currentRow < maxRow
Set originalData = Range(.Cells(currentRow,1),.Cells(currentRow+incrementRow-1,20)
your process to filter data
currentRow = currentRow +incrementRow
Wend
End Sub
子进程行()
Dim originalData()作为变体
Dim maxRow为长,currentRow为长,incrementRow为长
maxRow=ActiveSheet.Usedrange.Rows.Count
currentRow=1
增量行=5000
当currentRow
当然,您可以使用逐行方法,但我假设您使用数组变量来加速代码,因此我不建议使用逐行方法。逐行工作非常慢,因此对于如此大的数据集,这不是一个可行的解决方案 阵列无疑是一种选择,因此可以在以下两者之间进行选择:
- 这应该是正常的,直到非常大的数据集如内存不足错误最初不是来自数组本身的大小,而是来自工作表的读取
- 如果阵列本身的大小导致内存不足错误,则:
- 您将别无选择,只能使用64位Excel
- 或者(更好)重构您的过程,以分块处理数据(上面的选项2)
Option Explicit
Sub example()
Dim myCompletedataArr
Dim myTestDataRange As Range
Set myTestDataRange = ActiveSheet.UsedRange
loadDataInBatches myTestDataRange, myCompletedataArr
Debug.Assert False
End Sub
Sub loadDataInBatches(dataRange As Range, dataArr, Optional startRow As Long = 1, Optional rows As Long = 10000)
Dim endRow As Long, i As Long, j As Long
Dim dataArrLb1 As Long, dataArrLb2 As Long, batchArrLb1 As Long, batchArrLb2 As Long
Dim batchArr, batchRange As Range
If Not IsArray(dataArr) Then
ReDim dataArr(0 To dataRange.rows.Count - 1, 0 To dataRange.Columns.Count - 1)
End If 'otherwise assume dataArr is correctly dimensioned (for simplicity)
endRow = WorksheetFunction.Min(startRow + rows - 1, dataRange.rows.Count)
If endRow <= startRow Then Exit Sub
Set batchRange = dataRange.rows(startRow & ":" & endRow)
batchArr = batchRange.Value
'cache lower bounds as we use them a lot
dataArrLb1 = LBound(dataArr, 1): dataArrLb2 = LBound(dataArr, 2)
batchArrLb1 = LBound(batchArr, 1): batchArrLb2 = LBound(batchArr, 2)
For i = batchArrLb1 To UBound(batchArr, 1)
For j = batchArrLb2 To UBound(batchArr, 2)
dataArr(startRow - 1 + i + dataArrLb1 - batchArrLb1, j + dataArrLb2 - batchArrLb2) = batchArr(i, j)
Next j
Next i
Erase batchArr 'free up some memory before the recursive call
loadDataInBatches dataRange, dataArr, endRow + 1, rows
End Sub
选项显式
子示例()
Dim myCompletedataArr
将myTestDataRange设置为范围
设置myTestDataRange=ActiveSheet.UsedRange
LoadDataInBatch myTestDataRange,myCompletedataArr
Debug.Assert为False
端接头
子加载DataInBatch(dataRange作为Range,dataArr,可选startRow作为Long=1,可选行作为Long=10000)
暗尾行一样长,我一样长,j一样长
Dim DATALRLB1为长,DATALRLB2为长,batchArrLb1为长,batchArrLb2为长
Dim batchArr,batchRange作为范围
如果不是IsArray(dataArr),则
ReDim dataArr(0到dataRange.rows.Count-1,0到dataRange.Columns.Count-1)
“如果”结束,否则假设dataArr的尺寸正确(为简单起见)
endRow=WorksheetFunction.Min(startRow+rows-1,dataRange.rows.Count)
如果endRow以上所有建议都表明内存不足。我的计算机内存充足,我也曾尝试切换到64位Excel。我必须假设存在一个常见的Excel错误。对我来说,最安全(但不能保证每次都会发生)的方法如下:
由于Excel文件频繁崩溃这一内置效应,我将.xlsx文件中的数据和所有宏分离到加载项中。这种不方便的方法将崩溃几乎降至零。但是,每当我需要对加载项进行升级时,我都会先卸载加载项,然后修改加载项的源文件。保存此.xlsb文件,然后将其另存为。xlam。当我试图关闭外接程序的源文件(即.xlsb文件)时,系统会询问我是否要保存新副本或覆盖更改。无论我选择什么,我都会收到“内存不足”消息。有时我可以继续工作,有时Excel崩溃并启动新的。
也许这个描述解释了该消息与内存太少或分配的数据太多无关。另一种方法是分块攻击,比如说一次50000行,或者为您的计算机添加内存/RAM容量。我使用的是8 GB。在64位的windows上,您也可以有更多内存。我建议稍微增加一点不同的方法:您可以一次处理一行,而不是将所有内容加载到数组中。读取、检查、写入(如果正确),然后转到下一个。这将显著减少VBA的工作内存。您可能需要64位Excel。我因处理大量大型变量而出现内存不足的问题;切换到64位Excel非常有效。根本不需要升级硬件。
Option Explicit
Sub example()
Dim myCompletedataArr
Dim myTestDataRange As Range
Set myTestDataRange = ActiveSheet.UsedRange
loadDataInBatches myTestDataRange, myCompletedataArr
Debug.Assert False
End Sub
Sub loadDataInBatches(dataRange As Range, dataArr, Optional startRow As Long = 1, Optional rows As Long = 10000)
Dim endRow As Long, i As Long, j As Long
Dim dataArrLb1 As Long, dataArrLb2 As Long, batchArrLb1 As Long, batchArrLb2 As Long
Dim batchArr, batchRange As Range
If Not IsArray(dataArr) Then
ReDim dataArr(0 To dataRange.rows.Count - 1, 0 To dataRange.Columns.Count - 1)
End If 'otherwise assume dataArr is correctly dimensioned (for simplicity)
endRow = WorksheetFunction.Min(startRow + rows - 1, dataRange.rows.Count)
If endRow <= startRow Then Exit Sub
Set batchRange = dataRange.rows(startRow & ":" & endRow)
batchArr = batchRange.Value
'cache lower bounds as we use them a lot
dataArrLb1 = LBound(dataArr, 1): dataArrLb2 = LBound(dataArr, 2)
batchArrLb1 = LBound(batchArr, 1): batchArrLb2 = LBound(batchArr, 2)
For i = batchArrLb1 To UBound(batchArr, 1)
For j = batchArrLb2 To UBound(batchArr, 2)
dataArr(startRow - 1 + i + dataArrLb1 - batchArrLb1, j + dataArrLb2 - batchArrLb2) = batchArr(i, j)
Next j
Next i
Erase batchArr 'free up some memory before the recursive call
loadDataInBatches dataRange, dataArr, endRow + 1, rows
End Sub