【问题标题】:How to perform SumIf using VBA on an array in Excel如何在 Excel 中的数组上使用 VBA 执行 SumIf
【发布时间】:2014-09-04 00:41:03
【问题描述】:

我正在尝试想出最快的方法来在 Excel 中对具有大约 . 110'000 行。我想出了三种方法,但没有一种是令人满意的。

这是我尝试的第一个:PC 上的执行时间 100 秒!

    Sub Test1_WorksheetFunction()

Dim MaxRow As Long, MaxCol As Long
Dim i As Long
Dim StartTimer, EndTimer, UsedTime

StartTimer = Now()

With wsTest
    MaxRow = .UsedRange.Rows.Count
    MaxCol = .UsedRange.Columns.Count

    For i = 2 To MaxRow
        .Cells(i, 4) = WorksheetFunction.SumIf(wsData.Range("G2:G108840"), .Cells(i, 1), wsData.Range("R2:R108840"))
    Next i

End With

EndTimer = Now()
MsgBox (DateDiff("s", StartTimer, EndTimer))

End Sub

这是第二种方法:执行时间在 55 秒时稍微好一点

Sub Test2_Formula_and_Copy()

Dim MaxRow As Long, MaxCol As Long
Dim i As Long
Dim StartTimer, EndTimer, UsedTime

StartTimer = Now()

With wsTest
    MaxRow = .UsedRange.Rows.Count
    MaxCol = .UsedRange.Columns.Count

    Range("D2").Select
    ActiveCell.FormulaR1C1 = _
        "=SUMIF(Tabelle1[KUNDENBESTELLNR],Test!RC[-3],Tabelle1[ANZAHL NACHFRAGE])"
    Range("D2").Select
    Selection.AutoFill Destination:=Range("D2:D6285")
    Range("D2:D6285").Select
    Selection.Copy
    Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
        :=False, Transpose:=False

End With

EndTimer = Now()
MsgBox (DateDiff("s", StartTimer, EndTimer))

End Sub

第三次尝试:执行太慢了,根本没有完成。

Sub Test3_Read_in_Array()

Dim MaxRow As Long, MaxCol As Long
Dim SearchRange() As String, SumRange() As Long
Dim i As Long, j As Long, k
Dim StartTimer, EndTimer, UsedTime
Dim TempValue

StartTimer = Now()

With wsData
    MaxRow = .UsedRange.Rows.Count
    ReDim SearchRange(1 To MaxRow)
    ReDim SumRange(1 To MaxRow)
    For i = 1 To MaxRow
        SearchRange(i) = .Range("G" & (1 + i)).Value
        SumRange(i) = .Range("R" & (1 + i)).Value
    Next i
End With

With wsTest
    MaxRow = .UsedRange.Rows.Count
    For i = 2 To MaxRow
        For j = LBound(SearchRange) To UBound(SearchRange)
            k = .Cells(i, 1).Value
            If k = SearchRange(j) Then
            TempValue = TempValue + SumRange(j)
            End If
        Next j
        .Cells(i, 4) = TempValue
    Next i
End With


EndTimer = Now()
MsgBox (DateDiff("s", StartTimer, EndTimer))

End Sub

显然我还没有掌握 VBA(或任何其他编程语言)。有人可以帮助我提高效率吗?一定有办法!对吧?

谢谢!

【问题讨论】:

    标签: vba excel excel-2010


    【解决方案1】:

    当我想出以下解决方案时,我一直在寻找一种更快的方法来计算 Sumifs。您无需使用 Sumifs,而是将条件范围中使用的值连接为单个值,然后使用简单的 If 公式(结合一个范围排序)获得与使用 Sumifs 相同的结果。

    在我自己的例子中,使用具有 25K 行和 2 个标准范围的 Sumifs 进行评估平均需要 18.4 秒 - 使用 If 和 Sort 方法,平均需要 0.67 秒。

     Sub FasterThanSumifs()
        'FasterThanSumifs Concatenates the criteria values from columns A and B -
        'then uses simple IF formulas (plus 1 sort) to get the same result as a sumifs formula
    
        'Columns A & B contain the criteria ranges, column C is the range to sum
        'NOTE: The data is already sorted on columns A AND B
    
        'Concatenate the 2 values as 1 - can be used to concatenate any number of values
        With Range("D2:D25001")
            .FormulaR1C1 = "=RC[-3]&RC[-2]"
            .Value = .Value
        End With
    
        'If formula sums the range-to-sum where the values are the same
        With Range("E2:E25001")
            .FormulaR1C1 = "=IF(RC[-1]=R[-1]C[-1],RC[-2]+R[-1]C,RC[-2])"
            .Value = .Value
        End With
    
        'Sort the range of returned values to place the largest values above the lower ones
        Range("A1:E25001").Sort Key1:=Range("D1"), Order1:=xlAscending, _
        Key2:=Range("E1"), Order2:=xlDescending, Header:=xlYes
        Sheet1.Sort.SortFields.Clear
    
        'If formula returns the maximum value for each concatenated value match &
        'is therefore the equivalent of using a Sumifs formula
        With Range("F2:F25001")
            .FormulaR1C1 = "=IF(RC[-2]=R[-1]C[-2],R[-1]C,RC[-1])"
            .Value = .Value
        End With
    
        End Sub
    

    【讨论】:

    • 我在自己的电脑上进行了测试,得到了 100'000 行的 2 秒!好东西!感谢您的努力。
    • 不客气,毋庸置疑,同样的原则也适用于 COUNTIFS。
    • 这个答案在这里有一个更新版本:stackoverflow.com/questions/64939776/…
    【解决方案2】:

    试一试

    Sub test()
        StartTimer = Now()
        With ActiveSheet.Range("D2:D6285")
            .FormulaR1C1 = "=SUMIF(Tabelle1[KUNDENBESTELLNR],Test!RC[-3],Tabelle1[ANZAHL NACHFRAGE])"
            .Value = .Value
        End With
        EndTimer = Now()
        MsgBox (DateDiff("s", StartTimer, EndTimer))
    End Sub
    

    【讨论】:

    • 好的。知道了。替换我在相同单元格中写入值的乏味方法。 TIL 比一段时间以来一直在做的方式更有效率。惊人的!总而言之,运行代码所需的时间减少到 50 秒。比我目前最好的快 1 秒。
    • :) 很高兴它有帮助,我认为您现在受制于 Excel 及其函数处理速度
    • 我还尝试添加 application.screenupdating=false 奇怪的是它实际上使它变慢了(52 秒)
    • 为此,它不太可能产生任何影响。屏幕更新只会停止 excel 重新绘制,因为你没有循环,它只需要重新绘制两次
    • 凯尔,一个问题/想法。当我在数据透视表中执行相同的任务时,速度快如闪电(
    【解决方案3】:

    我的版本灵感来自 kevin999 的解决方案。

    ++ 适用于未排序的 sumif 标准
    ++ 将使行恢复到原来的顺序

    -- 不支持多个条件列

    请注意:包含条件和要汇总的数据的列必须一个接一个。

    Option Explicit
    
    Sub Execute()
    Call FasterThanSumifs(1)
    End Sub
    
    Private Sub FasterThanSumifs(Criteria As Long)
    'Expects two coloumns next to each other:
    'SumIf criteria (left side)
    'SumIf data range (right side)
    
    Dim SumRange, DataNumber, HelpColumn, SumifColumn, LastRow As Long
    SumRange = Criteria + 1
    DataNumber = Criteria + 2
    HelpColumn = Criteria + 3
    SumifColumn = Criteria + 4
    LastRow = UF_LetzteZeile()
    
    Columns(DataNumber).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
    Columns(HelpColumn).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
    Columns(SumifColumn).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
    
    'Remember data order
    Cells(2, DataNumber).Value = 1
    Cells(2, DataNumber).AutoFill Destination:=Range(Cells(2, DataNumber), Cells(LastRow, DataNumber)), Type:=xlFillSeries
    
    'Sort the range of returned values to place the largest values above the lower ones
    Range(Cells(1, Criteria), Cells(LastRow, SumifColumn)).Sort Key1:=Columns(Criteria), Order1:=xlAscending, Header:=xlYes
    ActiveSheet.Sort.SortFields.Clear
    
    'If formula sums the range-to-sum where the values are the same
    With Range(Cells(2, HelpColumn), Cells(LastRow, HelpColumn))
        .FormulaR1C1 = "=IF(RC[-3]=R[-1]C[-3], RC[-2] + R[-1]C,RC[-2])"
        '.Value = .Value
    End With
    
    'If formula returns the maximum value for each concatenated value match &
    'is therefore the equivalent of using a Sumifs formula
    With Range(Cells(2, SumifColumn), Cells(LastRow, SumifColumn))
        .FormulaR1C1 = "=IF(RC[-4]=R[+1]C[-4], R[+1]C, RC[-1])"
        .Value = .Value
    End With
    
    Columns(HelpColumn).Delete
    
    'Sort the range in the original order
    Range(Cells(1, Criteria), Cells(LastRow, SumifColumn)).Sort Key1:=Columns(DataNumber), Order1:=xlAscending, Header:=xlYes
    ActiveSheet.Sort.SortFields.Clear
    
    Columns(DataNumber).Delete
    
    End Sub
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-26
      • 1970-01-01
      • 2013-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多