如何使VBA循环(对于每个单元格,复制粘贴)更快?
我有一段代码占用了大量的实际运行时间。似乎这个循环实际上有时会使Excel无响应(对此不是100%确定,但在我看来,这是我在代码中遇到的最可能的罪魁祸首)。无论如何,我想优化这段代码,这样就不会花那么长时间 一些背景: 编辑:application.ScreenUpdate设置为false如何使VBA循环(对于每个单元格,复制粘贴)更快?,vba,excel,Vba,Excel,我有一段代码占用了大量的实际运行时间。似乎这个循环实际上有时会使Excel无响应(对此不是100%确定,但在我看来,这是我在代码中遇到的最可能的罪魁祸首)。无论如何,我想优化这段代码,这样就不会花那么长时间 一些背景: 编辑:application.ScreenUpdate设置为false 表(1)=原始数据 表(2)=面积表 进入循环前j=2 rng是范围,包括表1列CJ中的所有值减去标题 在sheet1列中,CJ是我要循环浏览的组件名称列表。对于每个ComponentName,我要过滤列AL
表(1)=原始数据
表(2)=面积表
进入循环前j=2
rng是范围,包括表1列CJ中的所有值减去标题
在sheet1列中,CJ是我要循环浏览的组件名称列表。对于每个ComponentName,我要过滤列AL并将列AL中的所有可见值(始终至少有>1个值)复制粘贴(转置)到工作表(2)。
每个组件名通常有大约1000-1200个组件名和10-240个值(与我复制粘贴到sheet2的值相同)。
For Each cell In rng
ComponentName = cell.Value
RawData.Range("A:CJ").AutoFilter field:=17, Criteria1:=ComponentName
RawData.Range("AL2", Range("AL2").End(xlDown)).Cells.SpecialCells(xlCellTypeVisible).Copy
AreaTable.Range("B" & j).PasteSpecial Transpose:=True
j = j + 1
Next cell
我可以对此循环进行哪些更改以更快地完成流程?构建一个
ComponentName
值数组,并过滤和复制/粘贴一次,而不是一千次
Dim v As Long, vCOMPNAMEs As Variant
With rng
ReDim vCOMPNAMEs(.Count)
For v = LBound(vCOMPNAMEs) To UBound(vCOMPNAMEs)
vCOMPNAMEs(v) = rng.cells(v + 1).Value2
Next v
End With
With RawData
.Range("A:CJ").AutoFilter Field:=17, Criteria1:=vCOMPNAMEs, Operator:=xlFilterValues
.Range("AL2", Range("AL2").End(xlDown)).Cells.SpecialCells(xlCellTypeVisible).Copy
AreaTable.Range("B" & j).PasteSpecial Transpose:=True
j = j + 1 '<~~?????
End With
Dim v为长,vCOMPNAMEs为变体
带rng
重拨vCOMPNAMEs(.Count)
对于v=LBound(vCOMPNAMEs)到UBound(vCOMPNAMEs)
vCOMPNAMEs(v)=随机单元格(v+1)。值2
下一个v
以
用原始数据
.Range(“A:CJ”)。自动筛选字段:=17,准则1:=vCOMPNAMEs,运算符:=xlFilterValues
.Range(“AL2”,Range(“AL2”).End(xlDown)).Cells.SpecialCells(xlCellTypeVisible)。复制
AreaTable.Range(“B”和j).Paste特殊转置:=真
j=j+1'构建一个包含ComponentName
值的数组,并过滤和复制/粘贴一次,而不是一千次
Dim v As Long, vCOMPNAMEs As Variant
With rng
ReDim vCOMPNAMEs(.Count)
For v = LBound(vCOMPNAMEs) To UBound(vCOMPNAMEs)
vCOMPNAMEs(v) = rng.cells(v + 1).Value2
Next v
End With
With RawData
.Range("A:CJ").AutoFilter Field:=17, Criteria1:=vCOMPNAMEs, Operator:=xlFilterValues
.Range("AL2", Range("AL2").End(xlDown)).Cells.SpecialCells(xlCellTypeVisible).Copy
AreaTable.Range("B" & j).PasteSpecial Transpose:=True
j = j + 1 '<~~?????
End With
Dim v为长,vCOMPNAMEs为变体
带rng
重拨vCOMPNAMEs(.Count)
对于v=LBound(vCOMPNAMEs)到UBound(vCOMPNAMEs)
vCOMPNAMEs(v)=随机单元格(v+1)。值2
下一个v
以
用原始数据
.Range(“A:CJ”)。自动筛选字段:=17,准则1:=vCOMPNAMEs,运算符:=xlFilterValues
.Range(“AL2”,Range(“AL2”).End(xlDown)).Cells.SpecialCells(xlCellTypeVisible)。复制
AreaTable.Range(“B”和j).Paste特殊转置:=真
j=j+1'在运行此操作之前关闭计算,因为每次筛选时,它都会重新计算工作簿,如果有很多公式,则会消耗处理器:
Application.Calculation = xlCalculationManual
For Each cell In Rng
ComponentName = cell.Value
RawData.Range("A:CJ").AutoFilter field:=17, Criteria1:=ComponentName
RawData.Range("AL2", Range("AL2").End(xlDown)).Cells.SpecialCells(xlCellTypeVisible).Copy
AreaTable.Range("B" & j).PasteSpecial Transpose:=True
j = j + 1
Next cell
Application.Calculation = xlCalculationAutomatic
运行此操作之前请关闭计算,因为每次筛选时,它都会重新计算工作簿,如果有大量公式,则会消耗处理器:
Application.Calculation = xlCalculationManual
For Each cell In Rng
ComponentName = cell.Value
RawData.Range("A:CJ").AutoFilter field:=17, Criteria1:=ComponentName
RawData.Range("AL2", Range("AL2").End(xlDown)).Cells.SpecialCells(xlCellTypeVisible).Copy
AreaTable.Range("B" & j).PasteSpecial Transpose:=True
j = j + 1
Next cell
Application.Calculation = xlCalculationAutomatic
大卫的建议是我将要发布的,这将非常有帮助。另外,请尝试此操作(不指定ComponentName)。未经测试,但应有效:
For Each cell In rng
RawData.Range("A:CJ").AutoFilter field:=17, Criteria1:=cell.Value
RawData.Range("AL2", Range("AL2").End(xlDown)).Cells.SpecialCells(xlCellTypeVisible).Copy
AreaTable.Range("B" & j).PasteSpecial Transpose:=True
j = j + 1
Next cell
它还可以更快地存储到阵列中。。。不幸的是我不知道你在复制多少个细胞。。。但我假设您在本例中复制2个单元格,根据您的需要进行更改。无论如何,您可以将结果存储到一个数组中,然后一次吐出所有结果,如下所示:
dim arr(300000,1)
For Each cell In rng
RawData.Range("A:CJ").AutoFilter field:=17, Criteria1:=cell.Value
arr(j,0) = RawData.Range("AL2")
arr(j,1) = RawData.Range("AL2").offset(1,0)
' etc.... do this for each (or create a loop to capture everything)
j = j + 1
Next cell
for j_ctr = 1 to j
AreaTable.Range("B" & j).value=arr(j_ctr,0)
AreaTable.Range("B" & j+1).value=arr(j_ctr,1)
next
除非昂贵的部分是自动过滤。。。有什么办法可以避免这种情况吗?大卫的建议就是我要发布的,这将非常有帮助。另外,请尝试此操作(不指定ComponentName)。未经测试,但应有效:
For Each cell In rng
RawData.Range("A:CJ").AutoFilter field:=17, Criteria1:=cell.Value
RawData.Range("AL2", Range("AL2").End(xlDown)).Cells.SpecialCells(xlCellTypeVisible).Copy
AreaTable.Range("B" & j).PasteSpecial Transpose:=True
j = j + 1
Next cell
它还可以更快地存储到阵列中。。。不幸的是我不知道你在复制多少个细胞。。。但我假设您在本例中复制2个单元格,根据您的需要进行更改。无论如何,您可以将结果存储到一个数组中,然后一次吐出所有结果,如下所示:
dim arr(300000,1)
For Each cell In rng
RawData.Range("A:CJ").AutoFilter field:=17, Criteria1:=cell.Value
arr(j,0) = RawData.Range("AL2")
arr(j,1) = RawData.Range("AL2").offset(1,0)
' etc.... do this for each (or create a loop to capture everything)
j = j + 1
Next cell
for j_ctr = 1 to j
AreaTable.Range("B" & j).value=arr(j_ctr,0)
AreaTable.Range("B" & j+1).value=arr(j_ctr,1)
next
除非昂贵的部分是自动过滤。。。有什么办法可以避免这种情况吗?您可以尝试以下方法:
Dim outputVal As Variant, chkRng As Variant, valRng As Variant
Dim i As Long, j As Long, k As Long
With rawdata
k = .Cells(Rows.Count, 38).End(xlUp).Row
chkRng = .Range("Q2:Q" & k).Value
valRng = .Range("AL2:AL" & k).Value
ReDim outputVal(rng.Count, 0)
For Each cell In rng.Value
k = 0
For i = LBound(chkRng) To UBound(chkRng)
If chkRng(i, 1) = cell Then
outputVal(j, k) = valRng(i, 1)
k = k + 1
If k > UBound(outputVal, 2) Then ReDim Preserve outputVal(rng.Count, k)
End If
Next
j = j + 1
Next
End With
With areatable: .Range(.Cells(1, 2), .Cells(rng.Count + 1, UBound(outputVal, 2) + 2)).Value = outputVal: End With
请用一份副本测试一下。。。没有真正的工作簿可能会把一切都搞砸。。。但它可能会以一个错误结束
请尝试一下,然后告诉我出了什么问题:)
编辑
用一个小表格测试了它,它工作得很好(而且速度也很快),但是:如果没有一个小示例工作簿,很难检查它是否也适用于您
EDIT2
工作方式:当寻找速度时,你需要知道一张纸需要做的每件事都很慢。因此,第一部分简单地获取所有值以检查/复制任何内容,并将它们放入变量中(这在读/写方面要快得多)。(chkRng
和valRng
)
然后我为输出生成一个变量(outputVal
)
知道只有1个值要检查(筛选),我还可以将该列与您的单元格进行比较。每次它找到匹配项时,另一个值(相同的位置)就会被放入输出值中(并根据需要调整值的大小)
最后,它一步将输出值粘贴到所需范围内
主要缺点:
-不会复制任何格式(仅复制值,但也可以更改为复制公式,而此处无需复制)
-您需要知道准确的范围(到小,值将丢失/到大,并且变量范围之外的每个单元格中都会出现错误代码)您可以尝试以下方法:
Dim outputVal As Variant, chkRng As Variant, valRng As Variant
Dim i As Long, j As Long, k As Long
With rawdata
k = .Cells(Rows.Count, 38).End(xlUp).Row
chkRng = .Range("Q2:Q" & k).Value
valRng = .Range("AL2:AL" & k).Value
ReDim outputVal(rng.Count, 0)
For Each cell In rng.Value
k = 0
For i = LBound(chkRng) To UBound(chkRng)
If chkRng(i, 1) = cell Then
outputVal(j, k) = valRng(i, 1)
k = k + 1
If k > UBound(outputVal, 2) Then ReDim Preserve outputVal(rng.Count, k)
End If
Next
j = j + 1
Next
End With
With areatable: .Range(.Cells(1, 2), .Cells(rng.Count + 1, UBound(outputVal, 2) + 2)).Value = outputVal: End With
请用一份副本测试一下。。。没有真正的工作簿可能会把一切都搞砸。。。但它可能会以一个错误结束
请尝试一下,然后告诉我出了什么问题:)
编辑
用一个小表格测试了它,它工作得很好(而且速度也很快),但是:如果没有一个小示例工作簿,很难检查它是否也适用于您
EDIT2
工作方式:当寻找速度时,你需要知道一张纸需要做的每件事都很慢。因此,第一部分简单地获取所有值以检查/复制任何内容,并将它们放入变量中(这在读/写方面要快得多)。(chkRng
和valRng
)
然后我为输出生成一个变量(outpu)