Excel VBA 在 table (listobject) 中工作时的性能问题

Excel VBA performance issues when working in table (listobject)

我正在寻找一些技巧来提高我的 vba 代码的性能,或者希望有人能发现我的问题所在,因为我自己也搞不清楚。

代码应该做什么:

代码有效。它做我想做的,但速度非常慢。添加大约 100 个项目的桶需要大约 22 秒。在 2000 件物品上,它已经 7 分钟了。但是,在某些情况下,我需要将存储桶放在 128 000 个条目旁边。但是我知道这可以用简单的公式来解决,但是数据 table 已经很大(2000 行和 400 列)并且有很多计算列。

我了解到 excel 的较新版本在需要访问 table 中的单元格时存在性能问题,但无法在任何地方找到合适的解决方案。感谢任何提示和技巧。

我已经尝试过的事情(但没有显着改善结果):

请参阅下面我的代码片段,如果您想了解任何其他信息,请告诉我。

Sub Buckets()

Application.Calculation = xlCalculationManual
Application.DisplayStatusBar = False
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Application.ScreenUpdating = False

Dim ws As Worksheet
Dim conf As Worksheet
Dim rcount As Long
Dim bucket1 As String
Dim bucket2 As String
Dim bucket3 As String
Dim bucket4 As String
Dim bucket5 As String
Dim bucket6 As String
Dim bucket7 As String
Dim bucket8 As String
Dim lim As Integer
Dim lim1 As Integer
Dim lim2 As Integer
Dim lim3 As Integer
Dim lim4 As Integer
Dim lim5 As Integer
Dim lim6 As Integer
Dim number As Double
Dim ScenNo  As Integer
Dim Datarange As Range
Dim Bucketrange As Range
Dim i As Integer

Set conf = Worksheets("Config")
Set ws = Worksheets("DATABASE")
Set Datarange = ws.Range("A9:A2008")
Set Bucketrange = ws.Range("B9:B2008")
rcount = ws.ListObjects("TABLE").ListColumns(7).Range.Find("*", searchorder:=xlByRows, LookIn:=xlValues, searchdirection:=xlPrevious).Row  

'B54 to B60 contains numbers from 15 up to 90 with a step of 15 minutes. The top value is 1000
With conf
    bucket1 = "Below " & .Range("B54").Value2 & " minutes"
    bucket2 = "Between " & .Range("B54").Value2 & " and " & .Range("B55").Value2 & " minutes"
    bucket3 = "Between " & .Range("B55").Value2 & " and " & .Range("B56").Value2 & " minutes"
    bucket4 = "Between " & .Range("B56").Value2 & " and " & .Range("B57").Value2 & " minutes"
    bucket5 = "Between " & .Range("B57").Value2 & " and " & .Range("B58").Value2 & " minutes"
    bucket6 = "Between " & .Range("B58").Value2 & " and " & .Range("B59").Value2 & " minutes"
    bucket7 = "Between " & .Range("B59").Value2 & " and " & .Range("B60").Value2 & " minutes"
    bucket8 = "Above " & .Range("B60").Value2 & " minutes"
    lim = .Range("B54").Value2
    lim1 = .Range("B55").Value2
    lim2 = .Range("B56").Value2
    lim3 = .Range("B57").Value2
    lim4 = .Range("B58").Value2
    lim5 = .Range("B59").Value2
    lim6 = .Range("B60").Value2
End With

For i = 9 To rcount
    If Cells(i, 16) = "" Or Cells(i, 16) = "Exclude" Then 'y - 1
    GoTo SKIPSTEP
    End If
            number = Datarange(i - 8, 1).Value2 'y - 1

            If number < lim Then
                    Bucketrange(i - 8, 1) = Buckets(1, 1).Value2
            ElseIf number >= lim And number < lim1 Then
                    Bucketrange(i - 8, 1) = Buckets(2, 1).Value2
            ElseIf number >= lim1 And number < lim2 Then
                    Bucketrange(i - 8, 1) = Buckets(3, 1).Value2
            ElseIf number >= lim2 And number < lim3 Then
                    Bucketrange(i - 8, 1) = Buckets(4, 1).Value2
            ElseIf number >= lim3 And number < lim4 Then
                    Bucketrange(i - 8, 1) = Buckets(5, 1).Value2
            ElseIf number >= lim4 And number < lim5 Then
                    Bucketrange(i - 8, 1) = Buckets(6, 1).Value2
            ElseIf number >= lim5 And number < lim6 Then
                    Bucketrange(i - 8, 1) = Buckets(7, 1).Value2
            Else
                    Bucketrange(i - 8, 1) = Buckets(8, 1).Value2
            End If
SKIPSTEP:

Next i
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
ActiveSheet.DisplayPageBreaks = True
End Sub

您的 for 循环是否会在每次迭代时增加列表对象 table 的大小?如果这就是正在发生的事情,那将是一个真正的减速带。我无法确定发生了什么,但它似乎覆盖了 Application.Calculate 并强制重新计算。

你提到不想用公式来做这件事。你在尝试查找方法吗?我想这也会陷入困境。您还可以显式地导出存储桶,假设 A2 是持续时间 date/time,秒数:

="between "
 &(A2-DATE(YEAR(A2),MONTH(A2),DAY(A2)))*24*60-MOD((A2-DATE(YEAR(A2),MONTH(A2),DAY(A2)))*24*60,15)
 &" and "
 &(A2-DATE(YEAR(A2),MONTH(A2),DAY(A2)))*24*60-MOD((A2-DATE(YEAR(A2),MONTH(A2),DAY(A2)))*24*60,15)+15
 &" minutes"

感谢@Rory,我将数字加载到一个数组中,现在它运行时间为 1.2 秒而不是 7 分钟。请参阅下面的代码片段的简化版本。我知道这可能会有一些额外的改进。一旦我可以稍微整理一下代码,我就会编辑我的答案。万分感谢@Rory,希望这对其他人也有帮助。

Sub Buckets()
Dim starttime As Double
Dim finish As Double
Dim endtime As Double
starttime = Timer()
Application.Calculation = xlCalculationManual
Application.DisplayStatusBar = False
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Application.ScreenUpdating = False

Dim ws As Worksheet
Dim conf As Worksheet
Dim rcount As Long
Dim bucket1 As String
Dim bucket2 As String
Dim bucket3 As String
Dim bucket4 As String
Dim bucket5 As String
Dim bucket6 As String
Dim bucket7 As String
Dim bucket8 As String
Dim lim As Integer
Dim lim1 As Integer
Dim lim2 As Integer
Dim lim3 As Integer
Dim lim4 As Integer
Dim lim5 As Integer
Dim lim6 As Integer
Dim number As Double
Dim ScenNo  As Integer
Dim x As Integer
Dim y As Integer
Dim Datarange() As Double
Dim Bucketrange() As String
Dim cell As Range

Set conf = Worksheets("Config")
With conf
    bucket1 = "Below " & .Range("B54").Value2 & " minutes"
    bucket2 = "Between " & .Range("B54").Value2 & " and " & .Range("B55").Value2 & " minutes"
    bucket3 = "Between " & .Range("B55").Value2 & " and " & .Range("B56").Value2 & " minutes"
    bucket4 = "Between " & .Range("B56").Value2 & " and " & .Range("B57").Value2 & " minutes"
    bucket5 = "Between " & .Range("B57").Value2 & " and " & .Range("B58").Value2 & " minutes"
    bucket6 = "Between " & .Range("B58").Value2 & " and " & .Range("B59").Value2 & " minutes"
    bucket7 = "Between " & .Range("B59").Value2 & " and " & .Range("B60").Value2 & " minutes"
    bucket8 = "Above " & .Range("B60").Value2 & " minutes"
    lim = .Range("B54").Value2
    lim1 = .Range("B55").Value2
    lim2 = .Range("B56").Value2
    lim3 = .Range("B57").Value2
    lim4 = .Range("B58").Value2
    lim5 = .Range("B59").Value2
    lim6 = .Range("B60").Value2
End With
Set ws = Worksheets("DATABASE")

x = 0
For Each cell In ws.Range("R9:R2008")
    ReDim Preserve Datarange(x)
    Datarange(x) = cell.Value2
            x = x + 1
Next cell
x = 0
Dim i As Variant
y = 0
For Each i In Datarange
            If i < lim Then
                    ReDim Preserve Bucketrange(y)
                    Bucketrange(y) = bucket1
                    y = y + 1
            ElseIf i >= lim And i < lim1 Then
                    ReDim Preserve Bucketrange(y)
                    Bucketrange(y) = bucket2
                    y = y + 1
            ElseIf i >= lim1 And i < lim2 Then
                    ReDim Preserve Bucketrange(y)
                    Bucketrange(y) = bucket3
                    y = y + 1
            ElseIf i >= lim2 And i < lim3 Then
                    ReDim Preserve Bucketrange(y)
                    Bucketrange(y) = bucket4
                    y = y + 1
            ElseIf i >= lim3 And i < lim4 Then
                    ReDim Preserve Bucketrange(y)
                    Bucketrange(y) = bucket5
                    y = y + 1
            ElseIf i >= lim4 And i < lim5 Then
                    ReDim Preserve Bucketrange(y)
                    Bucketrange(y) = bucket6
                    y = y + 1
            ElseIf i >= lim5 And i < lim6 Then
                    ReDim Preserve Bucketrange(y)
                    Bucketrange(y) = bucket7
                    y = y + 1
            Else
                    ReDim Preserve Bucketrange(y)
                    Bucketrange(y) = bucket8
                    y = y + 1
            End If
        Next i

ws.Range("S9:S2008") = Application.Transpose(Bucketrange)
Erase Datarange
Erase Bucketrange

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
ActiveSheet.DisplayPageBreaks = True
End Sub