VBA SumIf в массиве - PullRequest
       0

VBA SumIf в массиве

0 голосов
/ 02 марта 2020

Я пытаюсь написать код VBA для создания SumIf в массиве, поскольку мой рабочий лист содержит около 200 тыс. Строк и несколько формул на других листах с использованием формулы или .WorksheetFunction.SumIf занимает слишком много времени. Кроме того, мой рабочий лист не отсортирован, так как последние строки всегда содержат самые последние добавленные данные.

В моем листе 15 столбцов, но я использую только AB C D для суммирования. A, C, D = содержит текст, а столбец B содержит числа, которые я хочу суммировать.

Я пробовал следующий код, который прекрасно работает, но занимает около 5 минут для завершения вычислений.

Dim i As Long

With Sheets("Sheet1")

x = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 2 To x
    .Cells(i, 7).Value2 = Application.WorksheetFunction.SumIfs(.Range("B:B"), _
                    .Range("C:C"), .Range(("C") & i), _
                    .Range("A:A"), .Range(("A") & i), _
                    .Range("D:D"), .Range(("D") & i))
        Next i
End With

End Sub

Я начал работать с массивом VBA, чтобы заменить sumif, поскольку это будет намного быстрее, но мне не удается заставить его работать должным образом. Код, который я использую, приведен ниже.

Dim i As Long
Dim arrRAM As Variant
Dim arrType As Variant
Dim arrR As Variant
Dim arrO As Variant
Dim arrX As Variant
Dim arrY As Variant
Dim arrD As Variant
Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")

With Sheets("Sheet2")
    x = .Cells(.Rows.Count, 1).End(xlUp).Row
    arrRAM = .Cells(2, 2).Resize(x - 1).Value2
    arrType = .Cells(2, 3).Resize(x - 1).Value2
    arrR = .Cells(2, 1).Resize(x - 1).Value2
    arrO = .Cells(2, 4).Resize(x - 1).Value2
    arrX = .Cells(2, 5).Resize(x - 1, 2).Value2
    arrY = .Cells(2, 6).Resize(x - 1).Value2
    arrD = .Cells(2, 7).Resize(x - 1).Value2

For i = LBound(arrRAM, 1) To UBound(arrRAM, 1)
    arrY(i, 1) = arrType(i, 1) & arrR(i, 1) & arrO(i, 1)
    arrX(i, 1) = arrType(i, 1) & arrR(i, 1) & arrO(i, 1)
    arrX(i, 2) = arrRAM(i, 1)
Next i

For x = LBound(arrX, 1) To UBound(arrX, 1)
    dic(arrX(x, 1)) = arrX(x, 2)
Next x

tot = 0
For i = LBound(arrX, 1) To UBound(arrX, 1)
      If dic.Exists(arrY(i, 1)) Then
        tot = tot + arrX(i, 2)
    End If
    arrD(i, 1) = tot
    Next i

Debug.Print arrY(1, 1)
    .Cells(2, 6).Resize(UBound(arrD, 1)).Value2 = arrD

End With
End Sub

Идея заключалась в том, чтобы объединить A, C & D в один массив. Затем получите другой массив, который имеет объединенные значения + столбец B. Затем он должен найти объединенные значения из первого массива во втором (похоже, что он выполняет эту часть просто отлично), затем он должен сделать сумму.

Проблема возникает, когда мне нужно сложить значения, просто берется первое значение в столбце B, а затем добавляется следующее значение в первое. Ниже вы можете посмотреть результаты на примере данных для обычного кода SumIf Formula / First Vba и второго кода VBA.

Есть ли способ исправить мой код VBA, чтобы выводить те же результаты, что и в первой формуле one / sumif? ? Любая помощь приветствуется.

Blockquote

Ответы [ 3 ]

1 голос
/ 02 марта 2020

Попробуйте этот код, пожалуйста. Это быстро, использует массив и работает только в памяти. Все вычисленные значения записываются из массива сразу, в конце кода. Но это будет достаточно быстро для такого большого диапазона, только если одинаковые пары вхождений будут в большом числе ...

Private Sub testSumIfInArray() 'super tare, super fast
 Dim sh As Worksheet, arrI As Variant, arrF As Variant, lastR As Long
 Dim i As Long, j As Long, pCount As Long, d As Object

  Set sh = ActiveSheet
  lastR = sh.Range("A" & Cells.Rows.Count).End(xlUp).row
  arrI = sh.Range("A2:D" & lastR).value
  ReDim arrF(1 To UBound(arrI, 1), 1 To 1)
  Set d = CreateObject("Scripting.Dictionary")

  For i = 1 To lastR - 1
    If Not d.Exists(UCase(arrI(i, 1) & arrI(i, 3) & arrI(i, 4))) Then
        For j = 1 To lastR - 1
            If UCase(arrI(i, 1)) = UCase(arrI(j, 1)) And _
                   UCase(arrI(i, 3)) = UCase(arrI(j, 3)) And _
                       UCase(arrI(i, 4)) = UCase(arrI(j, 4)) Then
                pCount = pCount + arrI(j, 2)
            End If
        Next j
        d(UCase(arrI(i, 1) & arrI(i, 3) & arrI(i, 4))) = pCount
        arrF(i, 1) = pCount: pCount = 0
    Else
        arrF(i, 1) = d(UCase(arrI(i, 1) & arrI(i, 3) & arrI(i, 4)))
    End If
  Next
  sh.Range("E2").Resize(UBound(arrF, 1), 1).value = arrF
End Sub

Большое преимущество такого кода, поскольку вы сказали, что ваш лист обновляется новыми строками, чтобы запускать код, ища только значения в последних добавленных строках (разумеется, переданных во весь существующий диапазон). Таким образом, это будет очень быстро.

1 голос
/ 02 марта 2020

это работает с вариантами массивов:

     With Worksheets("Sheet1")
        Dim x As Long
        x = .Cells(.Rows.Count, 1).End(xlUp).Row

        Dim rngArr() As Variant
        rngArr = .Range(.Cells(2, 1), .Cells(x, 4)).Value

        Dim outArr As Variant
        ReDim outArr(1 To x, 1 To 1)

        Dim i As Long
        For i = LBound(rngArr, 1) To UBound(rngArr, 1)
            Dim j As Long
            For j = LBound(rngArr, 1) To UBound(rngArr, 1)
                If rngArr(i, 1) = rngArr(j, 1) And rngArr(i, 3) = rngArr(j, 3) And rngArr(i, 4) = rngArr(j, 4) Then
                    outArr(i, 1) = outArr(i, 1) + rngArr(j, 2)
                End If
            Next j
        Next i

        .Cells(2, 7).Resize(UBound(outArr, 1), 1).Value2 = outArr
    End With

enter image description here

0 голосов
/ 04 марта 2020

Мне удалось сделать это, обойдя этот ответ . И это довольно быстро с моим количеством данных. (1,5 секунды)

Код позволяет использовать несколько критериев, если вы их объединяете.

Он объединит их на другом листе, подсчитает сумму там и выведет результат на нужный лист + столбец.

Sub Sort1st()
Dim x As Long
Dim i As Long
Dim arr1() As Variant
Dim arr2() As Variant

Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Help"
With Sheets("Source")
    x = .Cells(.Rows.Count, 1).End(xlUp).Row
    arr1 = .Cells(1, 1).Resize(x, 33).Value2
End With
With Sheets("Help")
    arr2 = .Cells(1, 1).Resize(x, 2).Value2
End With
For i = 2 To x
    arr2(i, 1) = arr1(i, 5) & arr1(i, 31) & arr1(i, 32)
    arr2(i, 2) = arr1(i, 12)
    Next i
With Sheets("Help")
.Cells(1, 1).Resize(UBound(arr2, 1), UBound(arr2, 2)).Value2 = arr2
End With
Erase arr1, arr2
Call Sumifs(1)
End Sub

Private Sub Sumifs(Criteria As Long)
With Sheets("Help")
Dim SumRange, DataNumber, HelpColumn, SumifColumn, LastRow As Long
SumRange = Criteria + 1
DataNumber = Criteria + 2
HelpColumn = Criteria + 3
SumifColumn = Criteria + 4
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Columns(DataNumber).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Columns(HelpColumn).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Columns(SumifColumn).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove

Cells(2, DataNumber).Value = 1
Cells(2, DataNumber).AutoFill Destination:=Range(Cells(2, DataNumber), Cells(LastRow, DataNumber)), Type:=xlFillSeries

Range(Cells(1, Criteria), Cells(LastRow, SumifColumn)).Sort Key1:=Columns(Criteria), Order1:=xlAscending, Header:=xlYes
ActiveSheet.Sort.SortFields.Clear

With Range(Cells(2, HelpColumn), Cells(LastRow, HelpColumn))
    .FormulaR1C1 = "=IF(RC[-3]=R[-1]C[-3], RC[-2] + R[-1]C,RC[-2])"
End With

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

Range(Cells(1, Criteria), Cells(LastRow, SumifColumn)).Sort Key1:=Columns(DataNumber), Order1:=xlAscending, Header:=xlYes
ActiveSheet.Sort.SortFields.Clear

Columns(DataNumber).Delete
End With
Dim x As Long
Dim arr As Variant
With Sheets("Help")
x = .Cells(.Rows.Count, 1).End(xlUp).Row
    arr = .Cells(1, 3).Resize(x, 1).Value2
End With
With Sheets("Source")
.Cells(1, 35).Resize(UBound(arr, 1)) = arr
End With
Erase arr
Application.DisplayAlerts = False
Worksheets("Help").Delete
Application.DisplayAlerts = True
End Sub

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...