我有一个带有24深度嵌套IF语句的子
l=2
While l <= lmax 'lmax = 15000
If condition1 Then
If condition2a or condition2b Then
...
If condition24 then
ReDim Preserve propositions(UBound(propositions) + 1)
propositions(UBound(propositions)) = l
由于此子被调用250次,因此IF语句被称为250 * 15000,因此性能是一个大问题。 (宏在大约23秒内运行。)
当我写If condition2a or condition2b Then
时,如果条件 2a 为真,VBA是否检查条件 2b ? (即,应该 a 和 b 以便 a 不那么经常 b ?)
PS:当然,条件 1 vs 2 已经订购。
如@iDevlop所述,简短的回答似乎是VBA不允许“短路评估”(See this post)
我的问题是VBA 从表中读取/访问数据(而不是VBA计算IF语句。)。
解决方案在2D数组中加载数据。这个单一的修改使我的Sub运行速度提高了10倍以上(小于2秒vs 23秒)。
这是我的陈述的较短(17深)版本:
With Sheets("Sheet1")
lmax = .Cells(100000, 1).End(xlUp).Row 'Usually 14000
l = 2
While l <= lmax
If boolean_ignore_param1 Or Left(.Cells(l, 1).Formula, Len(param1)) = param1 Then
If boolean_ignore_param2 Or Left(.Cells(l, 2).Formula, Len(param2)) = param2Then
If (param_boolean_A And .Range("AF" & l).Formula = "Yes") Or (param_boolean_B And .Range("Ag" & l).Formula = "Yes") Then
If (.Cells(l, 6).Formula = "" Or .Cells(l, 6).Value - marge <= param3 Or param3= 0) Then
If (.Cells(l, 7).Formula = "" Or .Cells(l, 7).Value + marge >= param3 Or param3 = 0) Then
If (.Cells(l, 8).Formula = "" Or .Cells(l, 8).Value - marge <= param4 Or param4 = 0) Then
If (.Cells(l, 9).Formula = "" Or .Cells(l, 9).Value + marge >= param4 Or param4 = 0) Then
If (.Cells(l, 10).Formula = "" Or .Cells(l, 10).Value - marge <= param5 Or param5 = 0) Then
If (.Cells(l, 11).Formula = "" Or .Cells(l, 11).Value + marge >= param5 Or param5 = 0) Then
If (.Cells(l, 12).Formula = "" Or .Cells(l, 12).Value <= param6 Or param6 = 0) Then
If (.Cells(l, 13).Formula = "" Or .Cells(l, 13).Value >= param6 Or param6 = 0) Then
If (.Cells(l, 16).Formula = "" Or .Cells(l, 16).Value - marge <= param7 Or param7 = 0) Then
If (.Cells(l, 17).Formula = "" Or .Cells(l, 17).Value + marge >= param7 Or param7 = 0) Then
If (.Cells(l, 18).Formula = "" Or .Cells(l, 18).Value - marge <= param8 Or param8 = 0) Then
If (.Cells(l, 19).Formula = "" Or .Cells(l, 19).Value + marge >= param8 Or param8 = 0) Then
If (.Cells(l, 22).Formula = "" Or .Cells(l, 22).Value - marge <= param9 Or param9 = 0) Then
If (.Cells(l, 23).Formula = "" Or .Cells(l, 23).Value + marge >= param9 Or param9 = 0) Then
ReDim Preserve propositions(UBound(propositions) + 1)
propositions(UBound(propositions)) = l
答案 0 :(得分:3)
而不是或者,您可以将Select Case
与逗号分隔的条件列表一起使用,如下所示:
'If condition2a Or condition2b Then
Select Case True
Case condition2a, condition2b 'here comma means lazy 'OR' (like as OrElse in vb.net)
's = s + 10
Case Else
's = s + 20
End Select
此外,如果我们能看到您的代码,可能会有很多方面可以改善您的宏观性能。瞬间,数组的重新添加以向其添加一个项目可能在循环中耗费时间:
ReDim Preserve propositions(UBound(propositions) + 1)
你可以考虑在每次达到它的长度时将其ubound增加为10或100个项目(为下一个可能的用途保留一些空间),但是将实际的上限索引保留在一个变量中......
<强>更新强>
当你添加一部分代码时,我可以建议你为每个代码使用一些帮助函数,如下所示:
替换x<param
if
:
If (.Cells(l, 6).Formula="" Or .Cells(l, 6).Value-marge<=param3 Or param3=0) Then ...
像是:
If test(.Cells(l, 6).Value, marge, param3) Then ...
'or without '.Value': If test(.Cells(l, 6), marge, param3) Then ...
我们可以定义这个函数:
Function testLesser(v As Variant, marge As Double, param As Double) As Boolean
'testLesser = (v = "" Or v - marge <= param3 Or param3 = 0)
If v = "" Then
ElseIf v - marge <= param Then
ElseIf param = 0 Then
Else
testLesser = False: Exit Function
End If
testLesser = True
'** Another option (using Select Case):
'Select Case True
'Case v = "", v - marge <= param, param = 0
' testLesser = True
'Case Else
' testLesser = False
'End Select
End Function
和if
的其他类型(大于)类似:
If (.Cells(l, 7).Formula="" Or .Cells(l, 7).Value+marge>=param3 Or param3=0) Then ...
我们有:
Function testGreater(v As Variant, marge As Double, param As Double) As Boolean
'testGreater = (v = "" Or v + marge >= param Or param = 0)
If v = "" Then 'testLesser = True
ElseIf v + marge >= param Then 'testLesser = True
ElseIf param = 0 Then 'testLesser = True
Else
testLesser = False: Exit Function
End If
testLesser = True
'** Another option (using Select Case):
'Select Case True
'Case v = "", v + marge >= param, param = 0
' testLesser = True
'Case Else
' testLesser = False
'End Select
End Function
所以,代码看起来像:
'If (.Cells(l, 6).Formula = "" Or .Cells(l, 6).Value - marge <= param3 Or param3 = 0) Then
'If (.Cells(l, 7).Formula = "" Or .Cells(l, 7).Value + marge >= param3 Or param3 = 0) Then
'If (.Cells(l, 8).Formula = "" Or .Cells(l, 8).Value - marge <= param4 Or param4 = 0) Then
'If (.Cells(l, 9).Formula = "" Or .Cells(l, 9).Value + marge >= param4 Or param4 = 0) Then
'...
If testLesser(.Cells(l, 6), marge, param3) Then
If testGreater(.Cells(l, 7), marge, param3) Then
If testLesser(.Cells(l, 8), marge, param4) Then
If testGreater(.Cells(l, 9), marge, param4) Then
'...
我的真实测试表明它更快! (显然,它的代码也更易读)
注意:强>
非常重要的是安排if条件,以便尽快得到最终条件!例如,如果单元格值通常为空,请将该条件放在我们的测试函数中,但如果param = 0通常为真,则将其作为第一个条件检查...
这是x OR y
条件的规则。对于'x和y'标准,它是相反的!最罕见的情况必须首先快速过滤结果。在您的代码中,我看到您安排嵌套if从Cells(l, 6)
到Cells(l, 23)
。我不知道这对你的情况是否最好。这取决于你的数据和通常情况,所以考虑修改那些嵌套if
的顺序,如果你知道有些通常是假的......
另一个提示:
而不是使用With Sheets("Sheet1")
,将其缓存在变量中,这可以提高性能!
Dim mySheet As Worksheet
Set mySheet = Sheets("Sheet1")
With mySheet 'Sheets("Sheet1")
我的测试显示,这个简单的参考变化更快 10%。在处理工作表,范围,单元格时,您可能会想到其他类似的变化......
注意:如果我们可以将marge
定义为全局或工作表级别var,我们可以将其从函数参数中删除,但似乎它没有合理的效果......
上次更新:
@Ioannis在评论(see also this ref)中建议使用大量单元格时,最好将值加载到2D数组中并使用它而不是直接访问单元格:myArray = Sheets("Sheet1").Range("A1:AG15000").Value
然后使用该数组进行读/写:
myArray(row, col) = myArray(row, col) + 1
'row = 1 to UBound(myArray, 1) 'First array dimension is for rows
'col = 1 to UBound(myArray, 2) 'Second array dimension is for columns
最后,当你完成后,你可以反过来更新整个范围:
Sheets("Sheet1").Range("A1:AG15000") = myArray
答案 1 :(得分:0)