Pivot Table 2010 COUNT DISTINCT Salesman

SalimJ

New Member
Joined
Jul 8, 2013
Messages
22
Hi,

I have a Excel list for Sales data with 1 record per salesman/period.

I have created a pivot table in compact form from that list where in the Report Filter section, I have the period (year/month). In the Row Labels section, I have Cluster/Dept/Salesman. In the Values section, I want to display #Salesman, Total Sales, etc.

What I want to do is: whatever the number of periods I choose from the Report Filter, the #Salesman must display the correct number of salesmen. Actually, it is displaying the number of salesmen * the number of periods.

Any idea on how to solve this problem.

Many thanks.

Regards,
Salim.
 
Here's the code I used in your example file.

In the Sheet Code Module of the sheet that has the PivotTable...
Code:
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
    If Target.Name = "DummyForFilter" Then
        On Error GoTo CleanUp
        Application.EnableEvents = False
        Call UpdatePivotFromQuery
    End If
CleanUp:
    Application.EnableEvents = True
End Sub

In a Standard Code Module...
Code:
Option Explicit
    
Function UpdatePivotFromQuery()
'---Uses query to aggregate records from different periods
     
    '--update Unique Period PivotItems
    UpdateDummyPivot
    
    '--update Pivot based on Selected Periods
    UpdatePivotBasedOnPeriods
    
End Function

    
Function UpdatePivotBasedOnPeriods()
 '--Uses query that aggregates date for the Period(s)
 '     selected. Writes results to a range
 '     that is used as Pivot datasource
    Dim sSql As String, sWhereFilter As String
    Dim PT As PivotTable, PTDummy As PivotTable
    
    With Sheets("Pivot")
        Set PT = .PivotTables("PivotTable1")
        Set PTDummy = .PivotTables("DummyForFilter")
    End With
    
    '--read current filter selections from drop down list
    sWhereFilter = MakeFilterClause( _
        PTDummy.PivotFields("Period"))

    '--make query based on filter list
    sSql = Join$(Array( _
        "SELECT Activity, Cluster, Comp, Dept,", _
                "Employee, Status, Year,", _
                "SUM(DaysAbsence) AS SumOfDaysAbsence,", _
                "SUM(DaysActual) AS SumOfDaysActual,", _
                "SUM(DaysInjury) AS SumOfDaysInjury,", _
                "SUM(DaysLeaves) AS SumOfDaysLeaves,", _
                "SUM(DaysOpen) AS SumOfDaysOpen", _
            "FROM [Raw Data$]", _
            sWhereFilter, _
            "GROUP BY Activity, Cluster, Comp, Dept,", _
                "Employee, Status, Year" _
        ), vbCr)
    
    '--copy recordset to range, update name
    With Sheets("QueryResults")
        .Range("C2:N" & .Rows.Count).Clear
        Call OpenAndCopyFromRecordsetThisWB(sSql, _
            .Range("C2"))
        .Range("C1:N" & .Cells(.Rows.Count, _
            "C").End(xlUp).Row).Name = "PivotDataFromQuery"
    End With
    
    '--refresh pivot
    PT.PivotCache.Refresh
        
End Function


Function UpdateDummyPivot()
 '--Uses query and writes Unique Periods to a range
 '     that is used as Pivot datasource
    Dim sSql As String
    
    sSql = Join$(Array( _
        "SELECT DISTINCT Period", _
            "FROM [Raw Data$]", _
            "WHERE Period <> '(blank)' " _
       ), vbCr)
    
    '--copy recordset to range, update name
    With Sheets("QueryResults")
        .Range("A2:A" & .Rows.Count).Clear
        Call OpenAndCopyFromRecordsetThisWB(sSql, _
            .Range("A2"))
        .Range("A1:A" & .Cells(.Rows.Count, _
            "A").End(xlUp).Row).Name = "UniquePeriods"
    End With
    
    Sheets("Pivot").PivotTables("DummyForFilter") _
        .PivotCache.Refresh

End Function


Function OpenAndCopyFromRecordsetThisWB(sSql As String, _
        rDestination As Range)
'---Runs query copies resulting rst to rDestination

    Dim cnn As ADODB.Connection
    Dim rst As ADODB.Recordset
  
    Set cnn = New ADODB.Connection
    With cnn
      .Provider = "Microsoft.Jet.OLEDB.4.0"
      .Properties("Extended Properties").Value = "Excel 8.0"
      .Open ActiveWorkbook.FullName
    End With
         
    Set rst = New ADODB.Recordset
    
    rst.CursorLocation = adUseServer
    rst.Open Source:=sSql, _
        ActiveConnection:=cnn, _
        CursorType:=adOpenStatic, _
        LockType:=adLockReadOnly, _
        Options:=adCmdText

    rDestination.CopyFromRecordset rst
    rst.Close
    cnn.Close
    Set rst = Nothing
    Set cnn = Nothing

End Function


Function MakeFilterClause(PF As PivotField) As String
'--reads selected items from report filter PF and
'     makes WHERE clause that can be used in query
'     returns "" if (All) selected

    Dim sPage As String
    Dim i As Long, lCountSelected As Long
    Dim vSelected As Variant
    
    With PF
        vSelected = GetFilterList(PF)
        For i = LBound(vSelected) To UBound(vSelected)
            MakeFilterClause = MakeFilterClause & ",'" _
                    & vSelected(i) & "'"
        Next i
        MakeFilterClause = _
            "WHERE Period IN (" & Mid(MakeFilterClause, 2) & ")"
    End With
End Function


Function GetFilterList(PF As PivotField) As Variant
'--returns a Variant array of selected items in PivotField
    
    Dim sPage As String
    Dim i As Long, lCountSelected As Long, lCount As Long
    Dim vSelected As Variant
    Dim pi As PivotItem

    With PF
        lCount = .PivotItems.Count
        ReDim vSelected(1 To lCount)
        If .EnableMultiplePageItems Then
            For i = 1 To lCount
                If .PivotItems(i).RecordCount Then
                    If .PivotItems(i).Visible Then
                        lCountSelected = 1 + lCountSelected
                        vSelected(lCountSelected) = .PivotItems(i)
                    End If
                End If
            Next i
            ReDim Preserve vSelected(1 To lCountSelected)
        Else
            sPage = .CurrentPage
            If sPage = "(All)" Then
                vSelected = GetPivotItemList(PF)
            Else
                ReDim vSelected(1 To 1)
                vSelected(1) = sPage
            End If
        End If
    End With
    GetFilterList = vSelected
End Function

        
Function GetPivotItemList(PF As PivotField) As Variant
'--returns a Variant array of all PivotItems
'     from report filter PF

    Dim i As Long, lCount As Long, lCountExists As Long
    Dim vSelected As Variant
    
    With PF
        lCount = .PivotItems.Count
        ReDim vSelected(1 To lCount)
        For i = 1 To lCount
            If .PivotItems(i).RecordCount Then
                lCountExists = 1 + lCountExists
                vSelected(lCountExists) = .PivotItems(i)
            End If
        Next i
        If lCountExists <> lCount Then _
            ReDim Preserve vSelected(1 To lCountExists)
    End With
    GetPivotItemList = vSelected
End Function

The code should be thoroughly tested on a copy of your workbook before using for any real purpose.

The code assumes the names of the Sheets, PivotTables and Fields match yours.
The Report Filter for the Dummy Pivot needs to be placed with at least 2 empty rows between the Dummy Report Filter and the first filter of your Actual Pivot, otherwise Excel gives an error that you can't overlap Pivots. You could hide those two rows if you want.
 
Upvote 0

Excel Facts

How to show all formulas in Excel?
Press Ctrl+` to show all formulas. Press it again to toggle back to numbers. The grave accent is often under the tilde on US keyboards.
Jerry,

Thank you very much for spending time with my problem. I've done some basic testing, and it seems to work.

I will do more advanced testing and post a feedback.

I will also try to post some sample data, I will find how to do this. It seems quite complicated [I'm new to forums]. If someone could help, most welcome.

Best Regards.
 
Upvote 0
Jerry,

When the source data is updated, the QueryResults sheet is not updated. Any idea ?

Thanks.
 
Upvote 0
When the source data is updated, the QueryResults sheet is not updated. Any idea ?

Salim,

When you change the data in the Raw Data sheet, you'll need to refresh the query instead of just refreshing the Pivot.

You could do that by changing the Period Report Filter (you don't need to change anything, just click on the drop down then OK).

If you want this to happen when a user refreshes the PivotTable manually, you could modify the event code in the Sheet Code Module like this...

Code:
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
    If Target.Name = "DummyForFilter" [B]Or _
        Target.Name = "PivotTable1" [/B]Then
        On Error GoTo CleanUp
        Application.EnableEvents = False
        Call UpdatePivotFromQuery
    End If
CleanUp:
    Application.EnableEvents = True
End Sub

The trade-off of this is the query will be updated automatically whenever a change is made to the Pivot that triggers the PivotTableUpdate event, which includes expanding/collapsing groups, changing filters, and sorting.

In your example workbook, this process is pretty quick, so that might be okay. On a larger dataset, it might be better to use a different method to refresh the query only when needed.


I will also try to post some sample data, I will find how to do this. It seems quite complicated [I'm new to forums]. If someone could help, most welcome.

If you like, I'll upload your workbook to a hosting site and post a link. I'll save it in .xls format for Fazza and others that use xl2003.

I'll also replace the real names that in the dataset with Name1, Name2...

Please confirm that would be okay with you.
 
Upvote 0
hello, Salim

It would help if some sample data is posted. Both input data & corresponding output data.

regards

Hi Fazza, I'd be very interested to learn how you would suggest handling this.

This problem gave me an opportunity to work with ADO Recordsets which are relatively new to me.

My first approach was to directly set the PivotCache's Recordset with the results of the query.
That worked, but there were two side-effects and I don't know if those are known problems with that approach...

1. The resulting PivotTable had the Right-Click > Refresh option greyed-out.
2. The PivotItem.Visible property returned False for all PivotItems including those that were checked.

Is that something that you've seen as normal or did I do something screwy?

I don't want to hijack Salim's thread, so I'll start another thread with example code unless you identify that these are known side-effects.

Best regards, :)
 
Upvote 0
I don't understand the original question, Jerry. So don't know how I'd approach it.

If you set a pivot table's cache to a recordset, having the refresh option greyed out would seem appropriate. Really, what else could it be?

Pivot items visible all returning False: not something I've ever checked after assigning a recordset to a pivot cache. It isn't something I normally do. About the only instance I can recollect was a forum thread that I wrote some quick code to make a pivot table from multiple worksheets, but I don't normally work like that.

I haven't studied the details of this thread ('cause I didn't understand the requirement) however I suspect you have done nothing to cause a problem.

regards
 
Upvote 0
Jerry,

Thanks again for your help.

I will amend my workbook with the codes you've supplied and will revert back for a feedback.

I realize that I've embarked myself on something that I don't master and I'm completely lost in the codes (I'm not a programmer). In fact, I'm wondering whether I should continue due to maintenance purposes in the future !! Anyway, I will give it a try and well document the procedures.

Sure, you can upload my workbook to a hosting site. I would have done it myself, but don't know how !

Thanks to Fazza also for his interest in helping.

Regards.
 
Upvote 0
Salim's file can be downloaded at this link: https://app.box.com/s/6wrervqfg9hv1nm5kdr1

To clarify the OP question as I understand it, in the example file there is a Data Field labeled #Employees which is intended to be a count of how many Unique Employees there are for the selected criteria (by Cluster, Dept, Period of Time, etc).

The screen shot below shows a portion of the PivotTable when the Period Report Filter has two Periods selected ("2013/08", "2013/09").

10150844074_3709f577c4.jpg


The #Employees field shows a Count of 492 Employees for the Agriculture Total for the selected Periods.

The actual Count of Unique Employees in the dataset is 252 Employees for these two periods. The Count shown is roughly twice the desired result because most employees worked in both those periods so they are being double counted.

The desired result would be to have a report that looks exactly like the one in the example workbook that displays Counts of Unique Employees for whatever combination of Time Periods are selected.
 
Upvote 0
Thanks for posting the file, Jerry.

The download was going to take a very long time for me - not sure why it was so slow - so I abandoned it. However from your description, I have a reasonable understanding of the question and believe that whatever you've done is probably the best way to solve it. Well done. AFAIK there is no straightforward solution: one has to have a modified dataset particular to the fields & filters for any particular situation. Hence, whenever the particular 'question' changes - different fields/filters/whatever - the dataset needs to be re-generated. It would be interesting to see how the latest Excel 'distinct count' handles it: or even Power Pivot if it does.

Any solution I've had to similar questions (and it is only on the forum: I never have these questions in my line of work) in the past has only ever been to a specific fixed situation: not dynamic (as filters and fields vary in the pivot table).

regards
 
Upvote 0
On the way home I thought further of this question, Jerry. I guess that however a 'distinct count' is handled in the latest Excel version that is is along the lines of the approach you've taken. And there must be a ibt happening 'under the hood' for Excel to handle it. I can understand too why the programmers might have resisted providing this functionality for the last years. regards
 
Upvote 0

Forum statistics

Threads
1,217,386
Messages
6,136,278
Members
450,001
Latest member
KWeekley08

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top