Проверка, существует ли значение в столбце. CountIf или Autofilter-> Если отфильтрованный диапазон не Ничто - PullRequest
0 голосов
/ 04 мая 2020

Что более эффективно для 50k строк данных?

  1. Использование CountIf для всего столбца ("A: A") и проверка, существует ли значение, затем перейдите к коду.
  2. Используя автофильтр с критериями, если производимый диапазон не является ничем, тогда продолжайте с кодом.
        With Worksheets("Raw")
            project_name_counter = Application.WorksheetFunction.CountIf(.Range("A:A"), str_project_name)
        End With

        If project_name_counter > 0 Then

            With rng_raw
                    .AutoFilter 1, project_name
            End With
            ' code goes here

VERSUS

        With rng_raw
             .AutoFilter 1, str_project_name
        End With

        On Error Resume Next
            Set rng_filtered_raw = ws_raw.Range("J3", ws_raw.Cells(long_last_row_of_raw, 16)).SpecialCells(xlCellTypeVisible)
        On Error GoTo 0

        If Not rng_filtered_raw Is Nothing Then
        ' code code here

Я действительно ищу некоторые понимание, а не сарказм c ответов. Спасибо.

1 Ответ

2 голосов
/ 04 мая 2020

Матч против CountIf против Find feat. MicroTimer

Я провел некоторое исследование с использованием MicroTimer, написанного Чарльзом Уильямсом , которое я нашел в статье Поиск узких мест в Excel VBA .

Описание

Извините за результаты в комментариях к коду, но мне потребовалось некоторое время, чтобы создать следующую настройку:

  • fillColumns - заполняет диапазон, определенный Cols, строкой, определенной FillString.
  • TestAll - выполняет все четыре теста.
  • countFillChars - подсчитывает количество символов в FillString.
  • saveBook - сохраняет рабочую книгу.
  • TestWFMatch - проверяет WorksheetFunction версию Match.
  • TestWFMatch - проверяет Application версия Match.
  • TestCountIf - тестирует WorksheetFunction версию CountIf.
  • TestFind - тестирует Find Method.
  • MicroTimer - «измеряет» время, необходимое для завершения sh теста.

Результаты показывают время, прошедшее в секундах. Все тесты, за исключением теста, использующего сопоставленный результат в первой ячейке, выполняются для данных, в которых нет совпадения. Функции просто «l oop через ячейки» из 10 столбцов, заполненных одной и той же строкой, и «выполняют свою работу». Отсюда можно сделать вывод, что тестирование только «поцарапало» поверхность.

Заключение

Очевидные (для меня не очень научные c) выводы этих тестов следующие:

  • Когда Match используется, он автоматически останавливается, когда совпадение найдено, в то время как CountIf должно считаться до конца диапазона. Поэтому Match должно быть быстрее, но не может рассчитывать. Find (один из моих любимых) даже близко не подходит, но он может сделать кучу других вещей.
  • Я специально использовал те же начала строки, которые показали Мне кажется, что чем больше совпадений символов, тем медленнее становятся функции, что-то вроде:

    • 'This' - это даже близко не имеет 4 символа (особенно Match).
    • 'This string cont' - приближение (CountIf уже страдает, Match остается быстрым.)
    • Мне кажется, что функции сначала подсчитывают количество символов и только тогда проверяют, совпадают ли они.
    • Но шокер - это когда первый символ отличается. Затем CountIf «оживает», даже Match на том же количестве символов, на котором оно было только проверено.

Можно сделать еще много выводов и тесты должны быть выполнены. Инструменты здесь, остальное зависит от вас. Вы готовы? Начните с теста для AutoFilter, который я, к сожалению, здесь не включил.

Примечания:

14.5s против 0.0004s при совпадении не должно вас смущать, потому что это крайность: Match 10 раз нашел совпадение в первой ячейке, а CountIf 10 раз считает более миллиона совпадений.

Код

Поместите код в модуль. Вам просто нужно иметь Sheet1 (CodeName).

Option Explicit

' saveBook 12-15s if filled

' Fill String                     WFCountIf  WFMatch    Match  Find    FillC
' This                                  3.0   0.4      0.4      4.5      4.5
' This str                              4.7   0.4      0.4      4.5      4.7
' This string cont                      9.0   0.4      0.4      4.5      5.0
' This string contains 32c             11.5   0.4      0.4      4.5      5.2
' This string contains 32 chars!!!     14.5   0.0005   0.0007   0.001    5.5
' This string contains 32 chars!!.     14.5  14.5     14.5     19.0      5.5
' This string contained 32 chars! I added some more to make it 64.
'                                      11.0  11.0     11.0     72.0      6.4
' 32chars contained in this string      1.8   2.0      2.0      6.5      5.5

Private Const FillString As String = "32chars contained in this string"
Private Const Srch As String = "This string contains 32 chars!!!"
Private Const Cols As String = "A:J"
Private Const UB As Long = 9
Private i As Long
Private tStart As Double
Private tEnd As Double

Sub fillColumns()
    tStart = MicroTimer
        Sheet1.Columns(Cols).Value = FillString
    Debug.Print MicroTimer - tStart
End Sub

Sub TestAll()
    TestWFMatch
    TestMatch
    TestWFCountIf
    TestFind
End Sub

Sub countFillChars()
    Debug.Print Len(FillString)
End Sub

Sub saveBook()
    tStart = MicroTimer
        ThisWorkbook.Save
    Debug.Print MicroTimer - tStart
End Sub

Sub TestWFMatch()

    Dim ws As Worksheet: Set ws = Sheet1
    Dim Result(UB) As Boolean
    Dim FResult As Long

    tStart = MicroTimer
        With Application.WorksheetFunction
            For i = 1 To 10
                On Error Resume Next
                    FResult = _
                      .Match(Srch, ws.Range(Cols).Columns(i), 0)
                    Result(i - 1) = Err.Number = 0
                On Error GoTo 0
            Next i
        End With
    tEnd = MicroTimer - tStart

    Debug.Print String(10, "-") & vbCrLf & tEnd * 1000
    For i = 0 To 9: Debug.Print Result(i): Next

End Sub

Sub TestMatch()

    Dim ws As Worksheet: Set ws = Sheet1
    Dim Result(UB) As Boolean

    tStart = MicroTimer
        With Application
            For i = 1 To 10
                Result(i - 1) = _
                  Not IsError(.Match(Srch, ws.Range(Cols).Columns(i), 0))
            Next i
        End With
    tEnd = MicroTimer - tStart

    Debug.Print String(10, "-") & vbCrLf & tEnd * 1000
    For i = 0 To 9: Debug.Print Result(i): Next

End Sub

Sub TestWFCountIf()

    Dim ws As Worksheet: Set ws = Sheet1
    Dim Result(UB) As Boolean

    tStart = MicroTimer
        With Application.WorksheetFunction
            For i = 1 To 10
                Result(i - 1) = _
                  .CountIf(ws.Range(Cols).Columns(i), Srch) > 0
            Next i
        End With
    tEnd = MicroTimer - tStart

    Debug.Print String(10, "-") & vbCrLf & tEnd * 1000
    For i = 0 To UB: Debug.Print Result(i): Next

End Sub

Sub TestFind()

    Dim ws As Worksheet: Set ws = Sheet1
    Dim Result(UB) As Boolean

    tStart = MicroTimer
        With Application
            For i = 1 To 10
                Result(i - 1) = _
                  Not ws.Columns(i).Find(What:=Srch, After:=ws.Cells( _
                    ws.Rows.Count, i), LookIn:=xlFormulas) Is Nothing
            Next i
        End With
    tEnd = MicroTimer - tStart

    Debug.Print String(10, "-") & vbCrLf & tEnd * 1000
    For i = 0 To 9: Debug.Print Result(i): Next

End Sub


'START ****************************************************************** START'
Private Declare Function getFrequency Lib "Kernel32" _
        Alias "QueryPerformanceFrequency" (cyFrequency As Currency) As Long
Private Declare Function getTickCount Lib "Kernel32" _
        Alias "QueryPerformanceCounter" (cyTickCount As Currency) As Long
'******************************************************************************'
' Purpose:      Returns processor time. Used to determine small time intervals '
'               passed between places in code.                                 '
' Returns:      Processor time.                                                '
' Precedents:   getFrequency, getTickCount                                     '
'******************************************************************************'
Function MicroTimer() As Double
' Returns seconds.
    Dim cyTicks1 As Currency
    Static cyFrequency As Currency
    MicroTimer = 0
' Get frequency.
    If cyFrequency = 0 Then getFrequency cyFrequency
' Get ticks.
    getTickCount cyTicks1
' Seconds
    If cyFrequency Then MicroTimer = cyTicks1 / cyFrequency
End Function
'******************************************************************************'
' Example:                                                                     '
'------------------------------------------------------------------------------'
'Sub MicroTimerExample()
'    Dim dTime As Double
'    dTime = MicroTimer
'    Dim i As Long: For i = 1 To 1000000: Next
'    Debug.Print MicroTimer - dTime
'End Sub
'******************************************************************************'
' Source                                                                       '
'   Article                                                                    '
'     Title:    Excel 2010 Performance: Improving Calculation Performance
'     Link:     https://docs.microsoft.com/en-us/previous-versions/office/_
'               developer/office-2010/ff700515(v%3doffice.14)
'******************************************************************************'
' Found:        2018                                                           '
' Origin:       VBAddin.xlam                                                   '
'******************************************************************************'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...