Home > Enterprise >  How to Sum Values Based on Like-Items? (VBA)
How to Sum Values Based on Like-Items? (VBA)

Time:04-03

I am trying to sum values based on like-conditions, such as Date, the project, and the Bucket. Below is the test data that I am working with:

Date Project Bucket Hours
2022-03-28 ALN-1150 Time Bucket 1
2022-03-28 ALN-1150 Time Bucket 1
2022-03-28 ALN-1150 Time Bucket 2.5
2022-03-29 ALN-1150 Time Bucket 3
2022-03-29 ALN-1150 Time Bucket 2
2022-03-30 ALN-1500 Time Bucket 1
2022-03-30 ALN-1500 OMSH225 1

I tried to use the following to check how many like-items there were, but that only worked for the first date and stopped after that...

Sub addTime()

    Dim CountRows As Integer
    Dim Counter As Integer
    
    CountRow = 1
    Counter = 1
    
    
    Do While Len(Sheets("Formatting").Range("A1").Offset(rowOffset:=CountRow)) > 0
        Do While Sheets("Formatting").Range("A1").Offset(rowOffset:=Counter).Value = Sheets("Formatting").Range("A1").Offset(rowOffset:=(Counter   1)).Value _
            And Sheets("Formatting").Range("B1").Offset(rowOffset:=Counter).Value = Sheets("Formatting").Range("B1").Offset(rowOffset:=(Counter   1)).Value _
            And Sheets("Formatting").Range("C1").Offset(rowOffset:=Counter).Value = Sheets("Formatting").Range("C1").Offset(rowOffset:=(Counter   1)).Value _
            
            Counter = Counter   1

        Loop
        
        CountRow = CountRow   1
    Loop
    
    Debug.Print "Number of like Values: "   CStr(Counter)

End Sub

What I am expecting is:

Date Project Bucket Hours Total
2022-03-28 ALN-1150 Time Bucket 1 4.5
2022-03-28 ALN-1150 Time Bucket 1
2022-03-28 ALN-1150 Time Bucket 2.5
2022-03-29 ALN-1150 Time Bucket 3 5
2022-03-29 ALN-1150 Time Bucket 2
2022-03-30 ALN-1500 Time Bucket 1 1
2022-03-30 ALN-1500 OMSH225 1 1

Any help is greatly appreciated, as I have been stuck on this all day. Thank you

CodePudding user response:

It was not clear to me, if you wanted count or totals, so I have provided code which does both. It will be easy for you to amend to remove the values you don't want.

Dim LastRow As Integer
Dim i As Integer
Dim Count As Integer
Dim FirstRowInGroup As Long
Dim RunningTotal As Double

    LastRow = Cells(Rows.Count, 1).End(xlUp).Row
    FirstRowInGroup = 2
    Count = 1

    For i = 2 To LastRow - 1
        If i = FirstRowInGroup Then
            RunningTotal = Cells(i, 4)
        End If
        If Cells(i, 1) = Cells(i   1, 1) And _
        Cells(i, 2) = Cells(i   1, 2) And _
        Cells(i, 3) = Cells(i   1, 3) Then
            Count = Count   1
            RunningTotal = RunningTotal   Cells(i   1, 4)
        Else
            Cells(FirstRowInGroup, 5) = Count
            Cells(FirstRowInGroup, 6) = RunningTotal
            Count = 1
            FirstRowInGroup = i   1
        End If
    Next
    
    'Deal with last row being different
    If Cells(LastRow, 1) <> Cells(LastRow - 1, 1) Or _
    Cells(LastRow, 2) <> Cells(LastRow - 1, 2) Or _
    Cells(LastRow, 3) <> Cells(LastRow - 1, 3) Then
        Cells(LastRow, 5) = 1
        Cells(LastRow, 6) = Cells(LastRow, 4)
    End If

Please note that this solution depends on alike items being consecutive (grouped together), as in your sample data. If this is not the case, then you will need to sort the data (based on the three "like" columns) first. If for whatever reason that is impractical, then I can provide you with an alternative solution.

CodePudding user response:

If VBA is needed for whatever reason you could use the ADODB library to achieve that. The result will be independent of the order of the rows. Alike rows don't need to be grouped together. On the other hand the code will print for each single row the result on the right hand side which you might not want.

Option Explicit

Sub mySumIfS()

    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset
    Dim sSQL As String
    Dim xlFile As String
    xlFile = ThisWorkbook.FullName

    ' connect to this workbook
    With cn
        .Provider = "Microsoft.ACE.OLEDB.16.0"
        .ConnectionString = "Data Source=" & xlFile & "; Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
        .Open
    End With
    
    ' the result of this SQL staetement is the sum of hours grouped by Date and Bucket
    sSQL = "SELECT Date, Bucket, Sum(Hours) AS Total FROM [Sheet1$] GROUP BY Date, Bucket;"
    Set rs = cn.Execute(sSQL)
    
    
    Dim rg As Range, sngRow As Range, wks As Worksheet
    Set wks = ActiveSheet
    Set rg = wks.Range("A1").CurrentRegion
    Set rg = rg.Offset(1).Resize(rg.Rows.CountLarge - 1)
        
    ' Loop through the input data
    ' filter the result by Date and bucket
    ' print the total of hours to the right
    For Each sngRow In rg.Rows
        rs.Filter = "[Date] = " & sngRow.Cells(1, 1).Value & " AND [Bucket] = '" & sngRow.Cells(1, 3).Value & "'"
        sngRow.Cells(1, 1).Offset(, 4).Value = rs.Fields(2).Value
    Next

End Sub

If you do want to count instead of the total you only need to change the SQL statement slightly

sSQL = "SELECT Date, Bucket, Count(Hours) AS Total FROM [Sheet1$] GROUP BY Date, Bucket;"

CodePudding user response:

Perhaps try something like the below:

Sub addTime()

Dim SumHours As Double

Dim sh As Worksheet
Dim rng As Range
Dim row As Range

Dim arr(3) As Variant

Set sh = ThisWorkbook.Sheets("Formatting")
Set rng = sh.Range("A2:E8") 'expand as necessary or write function to obtain the total range to be checked

SumHours = 0
equal = False
arr(0) = 2
For Each row In rng.Rows

    equal = (arr(1) = row.Cells(1, 1) And arr(2) = row.Cells(1, 2) And arr(3) = row.Cells(1, 3))
    
    If (Not equal) Then
        sh.Cells(arr(0), 5) = SumHours
        arr(0) = row.row
        arr(1) = row.Cells(1, 1)
        arr(2) = row.Cells(1, 2)
        arr(3) = row.Cells(1, 3)            
        SumHours = row.Cells(1, 4)
    ElseIf equal Then
        SumHours = SumHours   row.Cells(1, 4)
    End If

Next

End Sub

It loops through all the rows in your range and stores the row of the first value, then keeps summing the values as long as all three cell values are equal?

You may need to write a function that returns a Range so that you can appropriately determine the valid range.

This yields the below result: enter image description here

  • Related