PTS Blog

Custom Solutions | Commercial Utilities | Training | Charts and Tutorials | PTS Blog

 

Main menu:

 
Peltier Tech
Chart Utilities

PTS Waterfall Chart Utility
PTS Cluster-Stack Column Chart Utility
PTS Box and Whisker Chart Utility
PTS Marimekko Chart Utility
PTS Dot Plot Utility
PTS Cascade Chart Utility

Subscribe

Site search

Subscribe

Site search


Recent Posts

Recently Commented

Popular Posts

Archive


 

Categories


 

Buy me a coffee

If this topic or the whole blog has been helpful, support further development by treating me to a cup of coffee.


Privacy Policy

Creative Commons License
Licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.

Referencing Pivot Table Ranges in VBA

by Jon Peltier
Peltier Technical Services, Inc., Copyright © 2009.
Licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.

I’ve posted several examples of manipulating pivot tables with VBA, for example, Dynamic Chart using Pivot Table and VBA and Update Regular Chart when Pivot Table Updates. These examples included specific procedures, and the emphasis was on the results of the manipulation.

I thought it would be helpful to show some of the mechanics of programming with pivot tables. One important part of this is referencing the various ranges within a pivot table by their special VBA range names (which are actually properties of the Pivot Table object).

I’ll illustrate these special ranges using this simple pivot table, which comes from an example formerly available on the Microsoft web site (I can no longer locate it).

Pivot Table

In VBA, you can reference a pivot table using this code in a procedure:

Dim pt As PivotTable
Set pt = ActiveSheet.PivotTables(1)

I’ve highlighted the various ranges using the indicated VBA commands.

TableRange1

pt.TableRange1.Select

Pivot Table - Table Range 1

TableRange2

pt.TableRange2.Select

Pivot Table - Table Range 2

RowRange

pt.RowRange.Select

Pivot Table - Row Range

ColumnRange

pt.ColumnRange.Select

Pivot Table - Column Range

DataLabelRange

pt.DataLabelRange.Select

Pivot Table - Data Label Range

DataBodyRange

pt.DataBodyRange.Select

Pivot Table - Data Body Range

PageRange

pt.PageRange.Select

Pivot Table - Page Range

Pivot Field LabelRange

pt.PivotFields("Years").LabelRange.Select

Pivot Table - Pivot Field Label Range

PivotField DataRange

pt.PivotFields("Years").DataRange.Select

Pivot Table - Pivot Field Data Range

Pivot Item LabelRange

pt.PivotFields("Years").PivotItems("2004").LabelRange.Select

Pivot Table - Pivot Item Label Range

Pivot Item DataRange

pt.PivotFields("Years").PivotItems("2004").DataRange.Select

Pivot Table - Pivot Item Data Range

pt.PivotFields("Order Date").PivotItems("Feb").DataRange.Select

Pivot Field LabelRange

The next few examples show the same ranges as above, after pivoting the table’s Years field from the columns area to the rows area.

pt.PivotFields("Years").LabelRange.Select

Pivot Table - Pivot Field Data Range

Pivot Item LabelRange

pt.PivotFields("Years").PivotItems("2004").LabelRange.Select

Pivot Table - Pivot Item Label Range

Pivot Item DataRange

pt.PivotFields("Years").PivotItems("2004").DataRange.Select

Pivot Table - Pivot Field Data Range

Complex Range

If a particular range isn’t specifically defined with a VBA property, you can use VBA range-extending properties EntireColumn and EntireRow and range operators Union and Intersect to define the range. For example:

Intersect(pt.PivotFields("Years").Pivotitems("2004").DataRange.EntireRow, pt.PivotFields("Order Date").DataRange).Select

Pivot Table - Complex Data Range

Possibly Related Posts:

Bookmark and share this entry:
  • Digg
  • del.icio.us
  • Facebook
  • Technorati
  • Twitter
  • StumbleUpon
  • Google Bookmarks
  • Reddit
  • MySpace
  • Slashdot
  • LinkedIn
  • Yahoo! Buzz

Learn how to create Excel dashboards.

Comments


Comment from Jeff Weir
Time: Monday, August 3, 2009, 4:02 am

Jon…this is an outstanding tutorial, even by your standards. Thank you, thank you, thank you.

This alone is worth the price of the several excel reporting and charting books I purchased that – rather annoyingly – don’t have this essential charting info in them.


Comment from Colin Banfield
Time: Monday, August 3, 2009, 10:32 am

An VBA object list described solely by visual worksheet examples. I’ve never seen a presentation quite like this. I think you’re on to something good here.


Comment from Jon Peltier
Time: Monday, August 3, 2009, 4:06 pm

Jeff & Colin -

When I went through this myself the first couple dozen times, I’d go to the Object Browser, read what it said, then when it wasn’t very clear, I’d run exactly the code I used here. Nothing like seeing something to understand it. Since few people are going to dig as deeply and obsessively as I did, it seemed like a good idea for a tutorial. If two smart guys like it, then I was on the right track.


Comment from AlexJ
Time: Monday, August 3, 2009, 10:37 pm

Jon,
Make it two and a half smart guys.


Comment from Doug Glancy
Time: Tuesday, August 4, 2009, 12:09 am

Jon,

This is great. I’m sure I’ll refer back here again.

A few months ago I created a pivot table class, partly to teach myself this stuff. As part of it I created functions to return the row and column grand total ranges, because I didn’t see any vba properties for them. Here’s the on for the Row Grand Totals. It seems to work. What do you think?

Function RowGrandTotals() As Range

With pt
If .RowGrand = True Then
Set RowGrandTotals = .DataBodyRange.Offset(0, .DataBodyRange.Columns.Count – 1).Resize(.DataBodyRange.Rows.Count + (1 * .ColumnGrand = True), 1)
End If
End With

End Function


Comment from Jon Peltier
Time: Tuesday, August 4, 2009, 6:56 am

Doug -

This works fine. Pretty much any range manipulation you can envision will be helpful. This article is to help people envision the pivot table structures in terms of properties of the Pivot Table object.


Comment from Jeff Weir
Time: Monday, September 21, 2009, 1:19 am

Hi Jon. I’ve a couple of questions for you re this post.

1. Where you have “Dim pt As Pivot Table” above, should this be “Dim pt As PivotTable” (i.e. PivotTable is one word with no space between Pivot and Table)?

In 2007 I need to take the space out to get your code to work.

2. In the case that you have the Pivottable’s Years field in the columns area, how would you select say the readins for Feb only for the years 2003 – 2005?

3. I have fields formatted as date/time information across the top of my pivottabel, and I amend your code accordingly, I get an error “Unable to get the PivotItems property of the PivotField class”. That is, your code can handle columns that are formatted as text, or even numbers that are formatted as text, but not numbers or dates formatted as dates.

For instance, The code works if I rename the pivot columns as text such as “a”, which obviously forces Excel to treat it as text which your vba can handle.

For instance, the SQL query that populates the pivot table shows a certain date as “1/11/2008 00:00″ (when viewing the raw SQL output in SQL Query Analyzer). My pivot table formats this as “1/11/2008″. But I get an error if I use a VBA statement like this:
pt.PivotFields(”Order Month/Year”).PivotItems(”1/11/2008 00:00″).DataRange.Select

…or this:
pt.PivotFields(”Order Month/Year”).PivotItems(”1/11/2008″).DataRange.Select

…or this:
pt.PivotFields(”Order Month/Year”).PivotItems(”39753″).DataRange.Select

However, if I overtype the field as something like “a” then this works:
pt.PivotFields(”Order Month/Year”).PivotItems(”a”).DataRange.Select

…and if I overtype the field as “1/11/2008″ (which sticks it in as text, not as a date) then this works:
pt.PivotFields(”Order Month/Year”).PivotItems(”a”).DataRange.Select

…and in both these cases, what I type in is automatically left-aligned as opposed to the original pivotfields, which are all right-aligned.

Interestingly, if I overtype 1/11/2008 with the number equivalent 39753, Excel doesn’t format it as text and the code fails. But if I then overtype 39753 with “a”, and then overtype “a” with 39753, excel treats it as text and the code works.

I take it from this that I need to amend the VBA in some way to tell it i’m looking for date/time information, not searching for text. Do you have any idea if this is the case, and if so, what kind of statement I need?

Thanks for any help.

Jeff


Comment from Jon Peltier
Time: Monday, September 21, 2009, 7:08 am

Jeff -

1. Yes, there is no space in PivotTable, good catch.

2. pt.PivotFields(”Order Date”).PivotItems(”Feb”).DataRange.Select

3. What I generally do is loop through the pivot items, and compare the pivot item caption to what I’m looking for:

For Each pi In pt.PivotFields("Order Month/Year").PivotItems
  If pi.Caption = Format(TheDate, "m/dd/yyyy") Then
  ' or If DateValue(pi.Caption) = TheDate Then
    '' FOUND IT
  End If
Next
 


Comment from Jeff Weir
Time: Monday, September 21, 2009, 10:54 pm

Hi Jon. That’s great. Sorry, 2 more questions for you on this. (You are probably going to cringe when you see how little I understand of VBA…I’m still working my way though Walkenbach’s Power Programming).

1. Once I’ve used the loop you’ve posted above in answer to my last question, how do I then select the PivotItem concerned? For instance, I tried each of the following modifications to no avail (put them in the THEN part of your code) :
pt.PivotFields(”Order Month/Year”).PivotItems(Format(”1/1/2009″, “m/dd/yyyy”)).DataRange.Select
pt.PivotFields(”Order Month/Year”).PivotItems(Pi.Caption).DataRange.Select
pt.PivotFields(”Order Month/Year”).PivotItem.DataRange.Select

2. In your example, how would I modify pt.PivotFields(”Order Date”).PivotItems(”Feb”).DataRange.Select
to select say Feb’s figure for 2004 and 2005, but not 2003?
This gives me Feb’s figure for 2004:
Intersect(pt.PivotFields(”Order Date”).PivotItems(”Feb”).DataRange, pt.PivotFields(”Years”).PivotItems(”2004″).DataRange).Select

But I cant’ simply add in another year like this:
Intersect(pt.PivotFields(”Order Date”).PivotItems(”Feb”).DataRange, pt.PivotFields(”Years”).PivotItems(”2004″,”2005″).DataRange).Select

…as I get the error “Wrong number of arguements or invalid property assignment”

Sorry for the stream of questions.
Regards, Jeff


Comment from Jon Peltier
Time: Tuesday, September 22, 2009, 7:14 am

1. Here’s what I had in mind:

For Each pi In pt.PivotFields("Order Month/Year").PivotItems
  If pi.Caption = Format(TheDate, "m/dd/yyyy") Then
  ' or If DateValue(pi.Caption) = TheDate Then
    pi.Select
    Exit For
  End If
Next
 

2. To select the data for 2004 and 2005, you would use something like this:

Union(pt.PivotFields("Years").PivotItems("2004").DataRange, _
    pt.PivotFields("Years").PivotItems("2005").DataRange).Select
 

So the Feb data for these two years would be

Intersect(pt.PivotFields("Order Date").PivotItems("Feb").DataRange, _
    Union(pt.PivotFields("Years").PivotItems("2004").DataRange, _
    pt.PivotFields("Years").PivotItems("2005").DataRange)).Select
 

Keep in mind that you do not need to select something if you just need to extract the data.


Comment from Jeff Weir
Time: Tuesday, September 22, 2009, 10:37 pm

Thanks Jon. Awesome.


Comment from LEM
Time: Tuesday, October 20, 2009, 8:13 am

Hey Jon,

Is there a way to apply a conditional format to a Pivot Table that would Bold Font and Highlight in Yellow entire rows that have met a certain condition in one of the columns?

Thank you!


Comment from Jon Peltier
Time: Tuesday, October 20, 2009, 9:04 am

You can use the same conditional formula as you would with a worksheet range (non-pivot). If the pivot table resizes, you’ll have to reapply or remove the conditional formatting, but if the pivot table refreshes without resizing, the conditional formatting works fine.


Comment from LEM
Time: Tuesday, October 20, 2009, 9:21 am

Hmmmm… Interesting, because when I refresh my pivot it will keep the conditional format on the first three columns, but the rest of the columns will lose the format when it is refreshed. Then if I try to Apply it again it does not work, and I have to re-enter the formula. The Pivot Table is not ‘resizing’ due to a filter for the Top 100 Items… So I was hoping there was some way to automate this…

Thanks again for the help!


Comment from Jon Peltier
Time: Tuesday, October 20, 2009, 9:28 am

I tested in Excel 2003 SP3, and the pivot table had three row fields and one data field. The data came from a list (”table” in 2007) and the data field values were volatile random numbers. Pressing F9 then refreshing the pivot table resulted in the formats updating to reflect the new values.


Comment from LEM
Time: Tuesday, October 20, 2009, 9:48 am

Sorry I keep having to post…

I am in Exel 2007, and my List Data is on a separate tab, I have the following in my PivotTable Field List:

1 Report Filter
2 Column Labels (One of which is ‘Values’ from the last Field List box)
3 Row Labels
9 Values

Even when I try pressing F9 (?) and refresh it will drop columns (from =$B$10:$V$109 to =$B$10:$D$109. (Only formatting my Row Labels, and losing formatting on all of the Values.

Because of this I was hoping I could include something within a Macro (that currently just does small things like column size adjustment) that would also update the conditional format Applied To Range.

Hope this makes sense :)


Comment from LEM
Time: Tuesday, October 20, 2009, 10:28 am

Just to add to the above:

And anytime I try to just ‘record’ my actions for conditional formatting nothing shows up…


Comment from Jon Peltier
Time: Tuesday, October 20, 2009, 11:33 am

I haven’t used conditional formatting much in 2007, and I haven’t programmed conditional formatting much in any version.

You could just do a generic formatting routine, following this pseudo-code:

for each row in pivottable-pivotfield-datarange-rows
..if row.cells(1,1).value > threshold value then
….row-entire row of pivot table-format-color and bold
..else
….row-entire row of pivot table-format-plain
..end if
next


Comment from LEM
Time: Tuesday, October 20, 2009, 12:36 pm

I would like to apologize in advance (and thank you for your patience!), because I am still very much a beginner in coding and trying to learn … But I am not sure exactly what to do… So far:

Sub PivotConditionalFormat()

‘ PivotConditionalFormat Macro

‘Bold and Highlight
pt.TableRange1.Select
Selection.FormatConditions.Delete
Selection.FormatConditions.Add Type:=xlExpression, Formula1:C =”=IF(C=”TEXT”,TRUE,FALSE)”
With Selection.FormatConditions(1).Font
.Bold = True
.ColorIndex = 3
End With

End Sub

But of course nothing works… I get errors thrown up all around the Formula and cannot get past this to even try it out – or see what other errors I have


Comment from Jon Peltier
Time: Tuesday, October 20, 2009, 9:03 pm

LEM -

If you have to use VBA to reapply conditional formatting every time the pivot table is refreshed, you might as well take the easy way out, skip the conditional formatting, and apply the formatting directly in the VBA routine. I’ve written up an example in Pivot Table Conditional Formatting with VBA. Thanks for the blog topic.


Comment from Si
Time: Wednesday, October 21, 2009, 4:19 am

Hi Jon

Very useful post,

A quick question if I may, I am using your code to automate the formatting of a set of sheets with pivot tables, all of which are different . My aim is to open a the workbook, change the source data for the day click refresh all pivots and have all the pivots format themselves.

I have been using the below code to format all the pivot tables.

Sub Format_Pivots()

Dim PT As PivotTable
Dim ws As Worksheet
Dim wb As Workbook
Set wb = ActiveWorkbook

For Each ws In Worksheets
For Each PT In ws.PivotTables
With PT.TableRange1
.Borders(xlInsideVertical).LineStyle = xlContinuous
.Borders(xlInsideHorizontal).Weight = xlContinuous
.Borders(xlEdgeTop).LineStyle = xlContinuous
.Borders(xlEdgeLeft).LineStyle = xlContinuous
.Borders(xlEdgeRight).LineStyle = xlContinuous
.Borders(xlEdgeBottom).LineStyle = xlContinuous
.Borders(xlEdgeTop).LineStyle = xlContinuous
.Borders(xlEdgeBottom).LineStyle = xlContinuous
.Borders(xlEdgeLeft).LineStyle = xlContinuous
.Borders(xlEdgeRight).LineStyle = xlContinuous
End With
Next PT
If Not (wb.Name Like “5 Week PTL*”) Then
ws.Columns(”B:Z”).ColumnWidth = 8.14
ElseIf Not (wb.Name Like “Obstetrics Waiting List*”) Then
ws.Columns(”B:Z”).ColumnWidth = 8.14
Else
ws.Columns(”C:Z”).ColumnWidth = 8.14
End If
Next ws

End Sub

However when I use pt.ColumnRange.Select & pt.DataBodyRange.Select I cannot get it to work unless I use your code specifying pivot table 1.

What am I missing?


Comment from Jon Peltier
Time: Wednesday, October 21, 2009, 12:02 pm

Si -

1. You’ve neglected to say which version of Excel you are using.

2. I see no references to ColumnRange or DataBodyRange in the code.

3. I removed the ColumnWidth stuff, and the rest of your code ran just fine in Excel 2003.


Comment from Rupert
Time: Tuesday, October 27, 2009, 2:06 am

Hi,
I was wondering if you know how to add comments to a pivot table via VBA.
I have data in a worksheet in the following format :

SalesStation,Date,Value,Comments
S01,01/10/2009,100
S01,02/10/2009,150,low sales on this day
S01,03/10/2009,120

and I would like to add any comments to the data field as a little red marker in the top right corner of the cell if there is a comment for that value.

Thanks


Comment from Jon Peltier
Time: Tuesday, October 27, 2009, 6:53 am

Rupert -

Like all expert VBA programmers, I considered your question, then fired up the macro recorder while adding a random comment to a random cell. Here is what it told me:

Sub AddComment()
'
' AddComment Macro
' Macro recorded 10/27/2009 by Jon Peltier
'

'
    Range("H6").AddComment
    Range("H6").Comment.Visible = False
    Range("H6").Comment.Text Text:="Jon Peltier:" & Chr(10) & "Low sales on this day"
End Sub
 

I trimmed the code to:

Sub AddCustomComment()
'
' AddComment Macro
' Macro recorded 10/27/2009 by Jon Peltier
'

'
    Range("H7").AddComment.Text Text:="Low sales on this day"
End Sub
 

It’s up to your code to identify the cell that should get the comment, and to remove comments when they are no longer applicable.


Comment from Rupert
Time: Tuesday, October 27, 2009, 7:50 pm

Thanks John,

This is what I have so far :

Private Sub addcomments(sheetname, pivottablename)
Dim c As Range, pvtitem As PivotItem, d As Range
With Sheets(sheetname).PivotTables(pivottablename)
If .RowRange.Count > 2 Then
For Each c In .PivotFields(”count of comments”).DataRange.Cells
If c.Value = 1 Then
Set d = Cells(c.Row, c.Column – 1)
d.AddComment “test”
Else
End If
Next
Else
End If
End With
End Sub

I can identify which cell to add the comment to but the problem is getting the text form the comments field actually into the d.addcomment “test” part above.

Do you have any ideas for it?

Thanks,
Rupert


Comment from Jon Peltier
Time: Tuesday, October 27, 2009, 8:29 pm

Rupert – Are the comments visible in the pivot table?


Comment from Rupert
Time: Tuesday, October 27, 2009, 9:05 pm

Yes, I get a comment in the cell.

The problems are :

1) I have to add the comments field as a data field – “count of comments” – and do a count on it so it is visible.
I then have to add a comment to the same row, previous column in the value data field if a value exists in the “count of comments” data field, and then hide the “countof comments” data field.

2) i cant seem to extract the actual comments text from the comments field into the inserted comment in the value data field.

Let me know if you want me to send a sample to you.

Cheers,
Rupert


Comment from Jon Peltier
Time: Tuesday, October 27, 2009, 9:14 pm

I’m thinking that putting the comments field into the rows area might help…


Comment from Rupert
Time: Friday, November 6, 2009, 12:22 am

Hi Jon,

Think I’ve got it. Heres what I did :

Sub insertcomments(sheetname, pipelinecode, processplantname, _
                   SalesStationCode, pivottablename)
  Dim datarange As Range, commentsrange As Range, custcode As Range
  Dim pprange As Range, ssrange As Range, catrange As Range, piperange As Range
  Dim d As Integer, mynames() As String
  Dim cust As String, datefield As String, pp As String
  Dim SSCode As String, cat As String, pipecode As String
  Dim pvtitem As PivotItem

  Worksheets("qryexcelexport").Select
  Set datarange = ActiveSheet.Range("A1").CurrentRegion
  ReDim mynames(datarange.Columns.Count)

  For c = 1 To datarange.Columns.Count
    mynames(c) = Cells(1, c).Value
  Next

  d = datarange.Rows.Count
  On Error Resume Next
  For e = 2 To d
    If Not IsEmpty(Cells(e, c - 1)) Then
      For f = 1 To c - 1
        Select Case mynames(f)
          Case "processPlantName"
            pp = Cells(e, f).Value
            Set pprange = Sheets(sheetname).PivotTables(pivottablename) _
                          .PivotFields("processPlantName").PivotItems(pp).datarange
          Case "SalesStationCode"
            SSCode = Cells(e, f).Value
            Set ssrange = Sheets(sheetname).PivotTables(pivottablename) _
                          .PivotFields("SalesStationCode").PivotItems(SSCode).datarange
          Case "CustomerCode"
            cust = Cells(e, f).Value
            Set custcode = Sheets(sheetname).PivotTables(pivottablename) _
                           .PivotFields("CustomerCode").PivotItems(cust).datarange
          Case "Category"
            cat = Cells(e, f).Value
            Set catrange = Sheets(sheetname).PivotTables(pivottablename) _
                           .PivotFields("Category").PivotItems(cat).datarange
          Case "PipelineCode"
            pipecode = Cells(e, f).Value
            Set piperange = Sheets(sheetname).PivotTables(pivottablename) _
                            .PivotFields("PipelineCode").PivotItems(pipecode).datarange
          Case "Date"
            datefield = Format(CDate(Cells(e, f).Value), "m/d/yyyy")
            Set daterange = Sheets(sheetname).PivotTables(pivottablename) _
                            .PivotFields("Date").PivotItems(datefield).datarange
          Case Else
        End Select
      Next

      With Sheets(sheetname).PivotTables(pivottablename)
        Set commentsrange = Intersect(pprange, ssrange, custcode, _
                                      catrange, piperange, daterange)
        commentsrange.AddComment Cells(e, c - 1).Value
      End With
    End If

    Set pprange = Nothing
    Set ssrange = Nothing
    Set custcode = Nothing
    Set catrange = Nothing
    Set piperange = Nothing
    Set daterange = Nothing
    Set commentsrange = Nothing
  Next

End Sub
 

Thanks for your time anyway, Rupert


Comment from Final Impact
Time: Thursday, November 12, 2009, 2:50 pm

Hi Jon and thanks for the above info it was helpful and insightful!
I am however at a loss as to how to do something simple and use cells from a sheets range like f2 to f32 to make items in a Pivot field ‘PivotItems Visible = True’. I’ve recorded macros and attempted to automate the process but I’m missing something. (Too many problems to list)

The PT comes from a table of test records with 30 columns and 15k rows. My PT is setup but I have to manually uncheck “Show All” and search for and check-off 200 serial numbers in column 1 of the PT every three days.
Can I point my vb code to a list or range cells and exclude everything in the column which is not in the column I specifiy (f2:f32 or A1 to A201) from another sheet?
PT headers are as follows: SERIAL_NUM, CAL_DATE, CAL_TIME, SLOT, ERROR_MESS, ect. and all I need is SERIAL_NUM visible equal to true for 200 of the 15k serial numbers available. Does this make sense?

Thanks for any input.
Final Impact

Write a comment

I welcome comments from my readers. If you have an opinion on this post, if you have a question or if there is anything to add, I want to hear from you. Whether you agree or disagree, please join the discussion.

If you want to include an image in your comment, post it on your own site or on one of the many free image sharing sites, and include a link in your comment. I'll download your image and insert the necessary html to display the image inline.

Read the PTS Blog Comment Policy.





Subscribe without commenting

PTS Waterfall Chart Utility PTS Cluster-Stack Column Chart Utility PTS Box and Whisker Chart Utility PTS Marimekko Chart Utility PTS Dot Plot Utility PTS Cascade Chart Utility

Create Excel dashboards quickly with Plug-N-Play reports.