Вычисление макроса VBA против вставки формулы Excel - PullRequest
0 голосов
/ 06 июня 2019

Я хотел бы задать вам этот запрос:

Я должен провести расчет для таблицы клиентов (скажем, это формула SumIf, суммирующая их платежи с другого листа, содержащего отдельные зарегистрированные транзакции)

Я сделал это с помощью макроса, который сначала вставляет формулу sumif в неиспользуемый столбец, затем копирует все значения столбца в основную таблицу и затем удаляет формулы.В конце концов, данные верны, и они не включают активные формулы, которые выполняются постоянно.

Таким образом, мой вопрос о такой ситуации будет такой: какая из них будет более эффективной:

То, что я сделал (макрос использовал для вставки формулы, копирования столбца, удаления всех)

Или какой-то другой способ вычисления в макросе?

1 Ответ

1 голос
/ 06 июня 2019

Я помещаю случайные значения в диапазон A1:F50000.

Вот пример для 4 различных способов вычисления суммы каждой строки:

Formula:       211,6254      'Fill formula and replace with values (range)
LoopCells:     3909,2864     'Calculate each cell value (loop)
LoopWsFunc:    3103,0727     'Calculate each cell with worksheet function (loop)
LoopArray:     159,9878      'Calculate in array (read/write) at once

Таким образом, на самом деле использование VBA и массивов - самый быстрый способ (в этом тестовом примере). Но это может зависеть от используемой вами формулы и количества данных на листе. Чтобы убедиться, что вы используете самый быстрый метод для своего конкретного случая, используйте более одного способа и проверьте, какой из них быстрее.

Вот код, который я использовал

Option Explicit

Public Sub TestFormula()
    Dim t As New cTimer
    t.StartCounter

    With Range("H1:H50000")
        .Formula = "=SUM(A1:F1)"  'write formula to all cells
        .Value = .Value           'replace formula by values
    End With

    Debug.Print "Formula:", t.TimeElapsed
End Sub


Public Sub TestLoopCells()
    Dim t As New cTimer
    t.StartCounter

    Dim iRow As Long
    For iRow = 1 To 50000   'calculate each cell by looping
        Cells(iRow, "H").Value = Cells(iRow, 1).Value + Cells(iRow, 2).Value + Cells(iRow, 3).Value + Cells(iRow, 4).Value + Cells(iRow, 5).Value + Cells(iRow, 6).Value
    Next iRow

    Debug.Print "LoopCells:", t.TimeElapsed
End Sub


Public Sub TestLoopWsFunc()
    Dim t As New cTimer
    t.StartCounter

    Dim iRow As Long
    For iRow = 1 To 50000   'Calculate each cell with sum function by looping
        Cells(iRow, "H").Value = WorksheetFunction.Sum(Range("A" & iRow & ":G" & iRow))
    Next iRow

    Debug.Print "LoopWsFunc:", t.TimeElapsed
End Sub


Public Sub TestLoopArray()
    Dim t As New cTimer
    t.StartCounter

    Dim InputArr() As Variant  'read data into array
    InputArr = Range("A1:F50000").Value

    ReDim OutputArr(1 To 50000, 1 To 1) As Variant 'generate output array

    Dim iRow As Long
    For iRow = 1 To 50000 'sum within the array
        OutputArr(iRow, 1) = InputArr(iRow, 1) + InputArr(iRow, 2) + InputArr(iRow, 3) + InputArr(iRow, 4) + InputArr(iRow, 5)
    Next iRow

    Range("H1:H50000").Value = OutputArr  'write the result from the array to the cells

    Debug.Print "LoopArray:", t.TimeElapsed
End Sub

Модуль класса cTimer для тестирования производительности

Option Explicit

Private Type LARGE_INTEGER
    lowpart As Long
    highpart As Long
End Type

Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LARGE_INTEGER) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LARGE_INTEGER) As Long

Private m_CounterStart As LARGE_INTEGER
Private m_CounterEnd As LARGE_INTEGER
Private m_crFrequency As Double

Private Const TWO_32 = 4294967296# ' = 256# * 256# * 256# * 256#

Private Function LI2Double(LI As LARGE_INTEGER) As Double
Dim Low As Double
    Low = LI.lowpart
    If Low < 0 Then
        Low = Low + TWO_32
    End If
    LI2Double = LI.highpart * TWO_32 + Low
End Function

Private Sub Class_Initialize()
Dim PerfFrequency As LARGE_INTEGER
    QueryPerformanceFrequency PerfFrequency
    m_crFrequency = LI2Double(PerfFrequency)
End Sub

Public Sub StartCounter()
    QueryPerformanceCounter m_CounterStart
End Sub

Property Get TimeElapsed() As Double
Dim crStart As Double
Dim crStop As Double
    QueryPerformanceCounter m_CounterEnd
    crStart = LI2Double(m_CounterStart)
    crStop = LI2Double(m_CounterEnd)
    TimeElapsed = 1000# * (crStop - crStart) / m_crFrequency
End Property

Источник: VBA Реализация QueryPerformanceCounter

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