简体   繁体   中英

VBA Drill down into relevant SUMIFS when multiple SUMIFS are nested in an IF statement

Good day,

I have a VBA script which allows me to drill down into a cell containing a SUMIFS, and then filters the raw data sheet to isolate the rows pertaining. The script works perfectly on a cell that has just one SUMIFS. However, some of my cells contain an IF statement with two SUMIFS (depending on what variable the IF is).

I am stuck trying to find a way to edit the VBA to first find the relevant SUMIFS in the IF statement, and then use the correct one to then filter.

My code:

  1. Double click script loaded on worksheet to trigger the macro (this is working fine)

     Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean) FilterBySUMIFs Target.Cells(1) End Sub
  2. Script loaded to Module for the SUMIFS

    Option Explicit Sub FilterBySUMIFs(r As Range) Dim v, ctr As Integer Dim intField As Integer, intPos As Integer Dim strCrit As String Dim rngCritRange1 As Range, rngSUM As Range Dim wksDataSheet As Worksheet If Not r.Formula Like "*SUMIFS(*" Then Exit Sub 'split formula by comma, strip the right paren v = Split(Left(r.Formula, Len(r.Formula) - 1), ",") 'the first criteria range is the 2nd element of the array Set rngCritRange1 = Range(v(LBound(v) + 1)) 'use first criteria range to get a reference to the data sheet With rngCritRange1 Set wksDataSheet = Workbooks(.Parent.Parent.Name).Worksheets(.Parent.Name) End With 'clear any existing filter, turn filtering on if needed With wksDataSheet If.AutoFilterMode And.FilterMode Then 'clear existing autofilter.ShowAllData ElseIf Not.AutoFilterMode Then 'display autofilter arrows rngCritRange1.CurrentRegion.AutoFilter End If End With 'set the filters For ctr = LBound(v) + 1 To UBound(v) If ctr Mod 2 <> 0 Then With wksDataSheet 'determine field in case table does not start in column A intField =.Range(v(ctr)).Column -.AutoFilter.Range.Columns(1).Column + 1 'use evaluate instead of range(v(ctr + 1)) 'so both cell-reference and hard-coded criteria are handled. strCrit = Evaluate(v(ctr + 1)).Range(v(ctr)).AutoFilter Field:=intField, Criteria1:=strCrit End With End If Next 'strip left paren and everything to left of it, get the sum range from first element of array intPos = InStr(1, v(LBound(v)), "(") Set rngSUM = Range(Replace(v(LBound(v)), Left(v(LBound(v)), intPos), "")) 'select the SUM range so total displays in status bar Application.Goto rngSUM ActiveWindow.ScrollRow = 1 End Sub Sub KV_FilterBySumIf() End Sub

My SUMIFS looks as follows:

=IF($C$6="ALL",SUMIFS(IS!Actual_Total,IS!Curr_Bud,H$9,IS!Master_Sub_Account,$C14),SUMIFS(IS!Actual_Total,IS!Curr_Bud,H$9,IS!Master_Sub_Account,$C14,IS!Project_Desc,$C$6))

I came up with a function that can split the IF formula into it's TRUE and FALSE parts and return relevant part based on the expression. So if the expression is true, the function returns the True part of the IF Formula.

The function I have made is not a robust function, and it only works if the given formula is in the structure of "=IF(< expression >, SUMIFS(...), SUMIFS(...))". And it evaluates the expression with the ActiveSheet .

Sub Example()
    Dim SumIfsFormula As String
    SumIfsFormula = "=IF($C$6=""ALL"",SUMIFS(IS!Actual_Total,IS!Curr_Bud,H$9,IS!Master_Sub_Account,$C14),SUMIFS(IS!Actual_Total,IS!Curr_Bud,H$9,IS!Master_Sub_Account,$C14,IS!Project_Desc,$C$6))"

    Debug.Print RelevantSumIfs(SumIfsFormula)
    'Output when TRUE : SUMIFS(IS!Actual_Total,IS!Curr_Bud,H$9,IS!Master_Sub_Account,$C14)
    'Output when FALSE : SUMIFS(IS!Actual_Total,IS!Curr_Bud,H$9,IS!Master_Sub_Account,$C14,IS!Project_Desc,$C$6)
End Sub

Function RelevantSumIfs(SumIfsFormula As String) As String
    Dim IfResult As Boolean
    IfResult = Application.Evaluate("=" & Split(Split(SumIfsFormula, "(")(1), ",")(0))
    
    Dim regex As Object
    Set regex = CreateObject("VBScript.RegExp")
    With regex
        .Global = True
        .IgnoreCase = True
        .Pattern = "SUMIFS\([^)]+\)"
    End With
    
    Dim Matches As Object
    Set Matches = regex.Execute(SumIfsFormula)
    
    Dim TargetSumIfs
    If IfResult Then
        TargetSumIfs = Matches(0)
    Else
        TargetSumIfs = Matches(1)
    End If
    
    RelevantSumIfs = TargetSumIfs
End Function

This function could be improved by changing the Regex pattern from explicitly searching for SUMIFS to searching for any function. Something like .Pattern = "[A-Za-z0-9]+\([^)]+\)" . But this may also match many other parts of the formula, including the surrounding IF(...) . Which is why I left it as .Pattern = "SUMIFS\([^)]+\)"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM