Excel VBA: для каждой настройки цикла

Изменить: добавлен образец необработанных данных ниже

Я запускаю отчет о претензиях каждый месяц и копирую данные во вкладку. Все данные организованы в столбцы, и я использовал электронную таблицу, полную SumProduct и CountIf, для подсчета и организации данных на основе разных наборов критериев, но обработка занимает слишком много времени, поэтому я пытаюсь написать VBA sub, чтобы сделать это более эффективно. Один из столбцов данных — «Наладчик домашнего офиса». Этот столбец в основном представляет собой список офисов, из которых исходит каждое требование. Я использовал AdvancedFilter, чтобы извлечь все уникальные значения в этом столбце и скопировать их на отдельную вкладку в столбце A. Затем в столбце C, под каждым местоположением, у меня есть список типов заявок или «позиций», обрабатываемых в каждом из них. офис. У меня нет проблем с установкой этой части. В столбце D мне нужно иметь возможность отображать количество каждой позиции в указанном месте. Вот где все Countif и SumProduct вступили в игру в моем старом шаблоне, который я использовал. Вот тут я наткнулся на загвоздку. Я пытаюсь использовать циклы For Each для подсчета каждого элемента строки в столбце B ниже первого местоположения, затем перейти к следующему местоположению в столбце A и повторить. Ниже приведен код, который я пробовал:

Private Sub CommandButton23_Click()

Dim linerngs As Range
Dim lineitem As Range
Dim lastlinerow As Long
Dim wsf
Dim TabLastRow
Dim claimstab As String
Dim officesrange As Range
Dim office As Range

claimstab = Sheet2.Range("F2") & " Claims"

TabLastRow = Sheets(claimstab).Cells(Sheets(claimstab).Rows.Count, "A").End(xlUp).Row

Set wsf = Application.WorksheetFunction

officeslastrow = Sheet2.Range("A" & Rows.Count).End(xlUp).Row
lastlinerow = Sheet2.Range("C" & Rows.Count).End(xlUp).Row

Set officerng = Range("A6:A" & officeslastrow).SpecialCells(xlCellTypeConstants, 23)
Set linerngs = Range("C7:C" & lastlinerow).SpecialCells(xlCellTypeConstants, 23)

For Each office In officerng
    For Each lineitem In linerngs
        If InStr(1, lineitem.Value, "IN") > 0 And InStr(1, lineitem.Value, "AOS") = 0 Then
            lineitem.Offset(0, 3) = Application.WorksheetFunction.SumProduct(wsf.CountIfs(Sheets(claimstab).Range("B2:B" & TabLastRow), office))
        End If
    Next lineitem
Next office


End Sub

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

пример того, что я сейчас получаю [пример того, что я сейчас получаю

пример того, что я пытаюсь получить [пример того, что я пытаюсь получить

Из первого примера видно, что я получаю значение «3» для всего. Я включил сводку местоположений и их значений. вы можете видеть, что последнее место в опорной точке, Южный Портленд, имеет счет 3.

Любая помощь будет принята с благодарностью.

пример необработанных данных [пример необработанных данных

Цель [задача

[Источник позицийсписок позиций заполняется с помощью пользовательской формы, запрашивающей ввод данных пользователем


person Graham Chandler    schedule 23.11.2015    source источник
comment
Небольшое примечание: при использовании нескольких листов всегда явно указывайте, какой лист вы используете, при использовании Cells(), Range(), Rows.Count и т. д. Например, измените свои строки на officeslastrow = Sheet2.Range("A" & Sheet2.Rows.Count).End(xlUp).Row (и то же самое для другой переменной).   -  person BruceWayne    schedule 24.11.2015
comment
Атланта, Джорджия должно быть 7?   -  person findwindow    schedule 24.11.2015
comment
да @findwindow, я ошибся. Атланта, Джорджия должно быть 7   -  person Graham Chandler    schedule 24.11.2015
comment
Это определенно не будет быстрее, чем формулы, использующие VBA, которые зацикливаются на диапазонах. Вероятно, это не будет быстрее, используя оптимизированный VBA (например, цикл по вариантным массивам). Лучше попросить помощи в оптимизации ваших формул, во всяком случае изначально.   -  person chris neilsen    schedule 24.11.2015
comment
@BruceWayne спасибо, буду иметь в виду   -  person Graham Chandler    schedule 24.11.2015
comment
@chrisneilsen пока что это молниеносно по сравнению с шаблоном формулы, который я использовал.   -  person Graham Chandler    schedule 24.11.2015
comment
Я хочу сказать, что если ваши формулы такие медленные, вы, вероятно, делаете их неправильно. Они тоже должны быть быстрыми. И если вы упорствуете в VBA, посмотрите на вариантные массивы, они на порядки быстрее, чем циклические диапазоны.   -  person chris neilsen    schedule 24.11.2015
comment
@chrisneilsen Я также пытаюсь защитить это от ошибок пользователя. мои коллеги в лучшем случае являются начинающими пользователями Excel.   -  person Graham Chandler    schedule 24.11.2015
comment
любая помощь в этом была бы потрясающей. если я смогу понять это, я смогу закончить весь проект   -  person Graham Chandler    schedule 24.11.2015


Ответы (1)


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

Первые два предположения (а вы знаете, что говорят о предположениях)

  1. Данные извлекаются из базы данных и возвращаются в виде строк, которые могут быть не в порядке. Например:

 ATLANTA, GA     IN-AK, HI  3  IN-CA  2  ...  IncidentOnly  4
 BOCA RATON, FL  IN-AK, HI  3  IN-CA  6  ...  IncidentOnly  5 
 ATLANTA, GA     IN-AK, HI  1  IN-CA  0  ...  IncidentOnly  2 
 ...
 AURORA, IL      IN-AK, HI  7  IN-CA  3  ...  IncidentOnly  4 
  1. Вы хотите, чтобы все страховые продукты суммировались для каждого офиса, а затем отображались в более красивом формате отчета.

If these assumptions are true (or close to true), you could create a HomeOffice class that had a property for each type of insurance, then simply loop through the rows of data in the raw report and add each HomeOffice object to a collection so you get a unique list of offices.

Пример из похожего по звучанию проекта, который я сделал:

Raw Data:
Mary    2   6
Sally   4   9
Mary    4   1
Sally   3   8
Joe     1   4
Bob     3   7
Mary    6   9
Sally   8   4
Bob     4   8
Joe     2   6
Joe     4   5

Formatted Data:
Mary       12      16
Sally      15      21
Bob         7      15
Joe         7      15

Для этого добавьте модуль класса (Insert -> Class Module) и измените его имя на HomeOffice. Вставьте этот код в класс (некоторые биты пропущены, поэтому он не такой длинный. Заполните, где необходимо, добавив свойство для каждого страхового продукта.)

Option Explicit

Private pOffice As String
Private pINAKI As Double
Private pINCA As Double
'... class properties left out for brevity
Private pIncidentOnly As Double


''''''''''''''''''''''
' Office property
''''''''''''''''''''''
Public Property Get Office() As String
    Office = pOffice
End Property
Public Property Let Office(Value As String)
    pOffice = Value
End Property

''''''''''''''''''''''
' INAKI property
''''''''''''''''''''''
Public Property Get INAKI() As Double
    INAKI = pINAKI
End Property
Public Property Let INAKI(Value As Double)
    pINAKI = Value
End Property

''''''''''''''''''''''
' INCA property
''''''''''''''''''''''
Public Property Get INCA() As Double
    INCA = pINCA
End Property
Public Property Let INCA(Value As Double)
    pINCA = Value
End Property

''''''''''''''''''''''
' Add other propertied for the different product types
''''''''''''''''''''''
' Follow the same format as the other properties

''''''''''''''''''''''
' IncidentOnly property
''''''''''''''''''''''
Public Property Get IncidentOnly() As Double
    IncidentOnly = pIncidentOnly
End Property
Public Property Let IncidentOnly(Value As Double)
    pIncidentOnly = Value
End Property

Теперь в подпрограмме CommandButton23_Click добавьте этот код (снова сокращенный для краткости, но, надеюсь, вы поняли):

Sub test()
    Dim col As Collection
    Dim r As Integer
    Dim c As Integer
    Dim HO As New HomeOffice

    'Collections can only have one Item, Key pair. 
    'We'll use the office location as the key to get a 
    'unique list of offices
    Set col = New Collection

    'Read in the raw data
    With Sheet1
        For r = 1 To .UsedRange.Rows.Count
            'Check if the location has an existing HomeOffice object 
            If InCol(col, .Cells(r, 1)) Then
                'It does so get the existing object and total the values
                Set HO = col.Item(.Cells(r, 1))
                HO.Office = .Cells(r, 1)
                HO.INAKI = HO.INAKI + .Cells(r, 2)
                HO.INCA = HO.INCA + .Cells(r, 3)
                ' more properties
                HO.IncidentOnly = HO.IncidentOnly + .Cells(r, 10)
                'We have to remove the existing object and add it again
                'to reflect the updated totals
                col.Remove (.Cells(r, 1))
            Else
                'The location hasn't been added yet so create and add it
                HO.Office = .Cells(r, 1)
                HO.INAKI = .Cells(r, 2)
                HO.INCA = .Cells(r, 3)
                ' More properties
                HO.IncidentOnly = .Cells(r, 10)
            End If
            col.Add HO, .Cells(r, 1)
            'Important to clear our object or our totals are wrong! :)
            Set HO = Nothing
        Next r
    End With

    'Now we simply loop through our collection of offices and
    'print out the totals.
    r = 6 'The first office starts on row 6 in your picture
    With Sheet2
        For Each HO In col
            .Cells(r, "A").Value = HO.Office
            .Cells(r + 1, "C").Value = "IN - AK, HI"
            .Cells(r + 1, "F").Value = HO.INAKI
            .Cells(r + 2, "C").Value = "IN - CA"
            .Cells(r + 2, "F").Value = HO.INCA
            'Continuing on for all 10 types
            .Cells(r + 10, "C").Value = "Incident Only"
            .Cells(r + 10, "F").Value = HO.IncidentOnly
            Set HO = Nothing
            r = r + 13 'So the next office starts 13 rows later...Row 19 in your pic
        Next
    End With
End Sub

Function InCol(col As Collection, key As Variant) As Boolean
    'Returns TRUE if the object is in the collection or FALSE if it is not
    Dim obj As New HomeOffice

    On Error GoTo err
    InCol = True
    'If the key doesn't exist, it throws an error and set the function to false
    Set obj = col(key)
    Set obj = Nothing
    Exit Function

err:
        InCol = False
End Function

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

person Tim    schedule 24.11.2015
comment
Я очень ценю ответ. Я попробую адаптировать эти функции к своему проекту. Единственная проблема, которую я вижу заранее, заключается в том, что не с каждого клиента взимается плата по одинаковым ставкам в одних и тех же штатах. пример: в то время как с одного клиента может взиматься одинаковая ставка за претензии, возникающие как в TX, так и в CA, с другого клиента могут взиматься разные ставки за TX и CA. этот процесс включает в себя массу динамических переменных, которые я должен учитывать. Создание универсального шаблона или, по крайней мере, шаблона, способного обслуживать 9/10 клиентов, оказалось настоящей рутиной. - person Graham Chandler; 24.11.2015
comment
Я ничего не знаю о массивах, но мне кажется, что я должен иметь возможность хранить необработанные данные в массиве и извлекать из него данные на основе переменных критериев (домашний офис регулировщика, тип линии, код покрытия, тип претензии, штат юрисдикции и т. д.). ). Моя главная цель — систематизировать необработанные данные Home Office Adjuster. Затем мне нужно будет указать макросу, что я ищу, и организовать его по критериям, которые я предоставляю. - person Graham Chandler; 24.11.2015
comment
Если вы хотите получить данные на основе критериев, вам нужна коллекция, а не массив. Коллекция, по сути, представляет собой массив из 2 элементов типа Key, Value. В моем ответе ключ — это офис, а значение — это объект, обладающий всеми свойствами ваших данных (например, грубо говоря, массив массивов). Таким образом, вы можете получить данные отдельного офиса с помощью кода set HO = col.Item("BOCA RATON, FL") Затем вы можете посмотреть индивидуальную статистику для этого офиса следующим образом: HO.MedicalOnly (при условии, что вы создали свойство MedicalOnly в модуле Class. - person Tim; 24.11.2015
comment
Из примера данных неясно, как разные состояния с разными ставками соотносятся с примером того, что вы пытаетесь получить. Если это основано на государстве юрисдикции, одним из ваших свойств HomeOffice может быть SoJ. Затем вы можете использовать оператор Select Case, чтобы определить скорость, которая должна применяться к этому состоянию. ЭГ, Select Case HO.SoJ Case "MN", "TX", "CA" Rate = 1.5 End Select - person Tim; 24.11.2015
comment
Добавил фото своей цели. Если это невозможно, то так тому и быть. Мне просто придется двигаться в другом направлении - person Graham Chandler; 24.11.2015
comment
Надеюсь, это не настоящие номера счетов! :) Но да, код, который я разместил, сделает это. Все дело в том, чтобы структурировать ваш цикл, чтобы поместить данные туда, где вы хотите. Я отредактирую код в своем ответе, чтобы он распечатывался, как ваша картинка. - person Tim; 24.11.2015
comment
лол, я только что удалил учетную запись #. в основном они были вымышленными, но лучше перестраховаться, чем сожалеть. Спасибо! - person Graham Chandler; 24.11.2015
comment
это кажется мне статичным. Позиции часто отличаются для каждого клиента. Позиции, состояния, ставки — все это переменные, и я не понимаю, как это дает мне необходимую гибкость. Я добавил еще одно изображение, которое показывает, где список позиций заполняется пользовательской формой и откуда импортируется. - person Graham Chandler; 24.11.2015
comment
Ааа... Тогда приношу свои извинения за то, что направил вас по ложному пути. Судя по картинкам, каждый офис (Атланта, Аврора, Бока и т. д.) имел точно такие же 10 категорий. Это может быть не лучший метод для ваших нужд. Извиняюсь! :( - person Tim; 24.11.2015
comment
не беспокойтесь, я ценю усилия, несмотря ни на что. Я должен был быть более ясным впереди. - person Graham Chandler; 24.11.2015