Excel VBA: สำหรับการตั้งค่าแต่ละวง

แก้ไข: เพิ่มตัวอย่างข้อมูลดิบด้านล่าง

ฉันจัดทำรายงานการเคลมทุกเดือนและคัดลอกข้อมูลลงในแท็บ ข้อมูลทั้งหมดถูกจัดระเบียบเป็นคอลัมน์ และฉันใช้สเปรดชีตที่เต็มไปด้วย SumProduct และ CountIf เพื่อนับและจัดระเบียบข้อมูลตามเกณฑ์ชุดต่างๆ แต่ใช้เวลานานเกินไปในการประมวลผล ดังนั้นฉันจึงพยายามเขียน VBA ย่อยเพื่อให้บรรลุผลสำเร็จได้อย่างมีประสิทธิภาพมากขึ้น ข้อมูลคอลัมน์หนึ่งคือ "Adjuster Home Office" คอลัมน์นี้โดยพื้นฐานแล้วคือรายชื่อสำนักงานที่การเรียกร้องแต่ละครั้งเกิดขึ้น ฉันใช้ AdvancedFilter เพื่อแยกค่าที่ไม่ซ้ำกันทั้งหมดในคอลัมน์นี้ และคัดลอกไปยังแท็บแยกต่างหากในคอลัมน์ A จากนั้น ในคอลัมน์ C ใต้แต่ละสถานที่ ฉันมีรายการประเภทการอ้างสิทธิ์หรือ "รายการโฆษณา" ที่จัดการในแต่ละแห่ง สำนักงาน. ฉันไม่มีปัญหาในการตั้งค่าส่วนนี้ ในคอลัมน์ D ฉันจะต้องสามารถแสดงจำนวนแต่ละรายการในตำแหน่งที่กำหนดได้ นี่คือจุดที่ Countif และ SumProduct ทั้งหมดเข้ามามีบทบาทในเทมเพลตเก่าของฉันที่ฉันใช้ นี่คือจุดที่ฉันได้รับอุปสรรค์ ฉันกำลังพยายามใช้ For Each loops เพื่อนับแต่ละรายการในคอลัมน์ 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

ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมอย่างมาก.

ตัวอย่างข้อมูลดิบ [example of raw data

วัตถุประสงค์ [objective

[แหล่งที่มาของรายการโฆษณารายการของรายการโฆษณาเสร็จสมบูรณ์แล้วซึ่งสร้างขึ้นโดยแบบฟอร์มผู้ใช้ที่ขอให้ผู้ใช้ใส่ข้อมูล


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
Atlanta, GA ควรเป็น 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

ในการดำเนินการนี้ ให้เพิ่มโมดูลคลาส (แทรก -> โมดูลคลาส) และเปลี่ยนชื่อเป็น 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
ฉันไม่มีความรู้เกี่ยวกับอาร์เรย์ แต่ดูเหมือนว่าฉันควรจะสามารถจัดเก็บข้อมูลดิบในอาร์เรย์และดึงข้อมูลออกมาตามเกณฑ์ตัวแปรได้ (Adjuster Home Office, Line Type, Coverage Code, Claim Type, State of Jurisdiction ฯลฯ ). วัตถุประสงค์หลักของฉันคือการจัดระเบียบข้อมูลดิบโดย Adjuster Home Office จากนั้น ฉันจะต้องสามารถบอกแมโครได้ว่าฉันต้องการอะไร และจัดระเบียบตามเกณฑ์ที่ฉันให้ไว้ - person Graham Chandler; 24.11.2015
comment
หากคุณต้องการดึงข้อมูลตามเกณฑ์ คุณต้องการรวบรวม ไม่ใช่อาร์เรย์ โดยพื้นฐานแล้วคอลเลกชันคืออาร์เรย์ 2 องค์ประกอบประเภท Key, Value ในคำตอบของฉัน กุญแจสำคัญคือสำนักงานและค่าเป็นวัตถุที่มีคุณสมบัติทั้งหมดของข้อมูลของคุณ (เช่น อาร์เรย์ของอาร์เรย์อย่างอิสระ) ดังนั้น คุณสามารถดึงข้อมูลของแต่ละสำนักงานขึ้นมาด้วยโค้ด set HO = col.Item("BOCA RATON, FL") จากนั้น คุณสามารถดูสถิติแต่ละแห่งสำหรับสำนักงานนั้นได้ดังนี้: HO.MedicalOnly (สมมติว่าคุณสร้างคุณสมบัติ MedicalOnly ใน Class Module - 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