Accessing Pivot tables from another Sheet in VBA - vba

Very novice user here, just getting my feet wet. Please excuse this if it's an idiotic question - I'm quite sure what I'm doing yet.
I'm creating a userform with VBA and Excel. I've been successful in learning how to pull information from a pivot table so far, but I've run into a hitch.
When run my userform from another sheet it can't find the pivot table. Here's the code I'm using.
Set PT = ActiveSheet.PivotTables(1)
Works perfectly when I'm one that sheet, but I envision a scenario where I have multiple pivots on different sheets and will want to call info and cross-reference the data.
I thought maybe Set PT = ActiveWorkbook.PivotTables(1) would work, but of course it doesn't. Obviously I don't yet quite understand how to use a pivot table variable.
Does anyone know how I might go about doing this? Thanks so much.

If you open UserForm, workbook with UserForm open will be the active one. If I understand correctly, you want to refer to 1st object of PivotTables collection on a different sheet. To do so, define path of the file with pivot table, example below:
Dim path as string
Dim wbk as workbook
Dim PT As PivotTable
path= "C:\folder1\folder2\yourfile.extension"
set wbk = workbooks.open(path)
Set PT = wbk.worksheets("Sheet1").PivotTables(1)
'do your actions, save the file if you want to keep changes
wbk.close
set wbk = nothing
Alternatively work on all Pivot Tables in your worksheet, instead of:
Set PT = wbk.worksheets("Sheet1").PivotTables(1)
You can use a loop through collection, this example refreshes tables:
For Each PT In wbk.worksheets("Sheet1").PivotTables
PT.RefreshTable
Next PT

Related

PivotCache Refresh from Access

I am automating some Excel file creation from Access. I need help with dynamically resetting the pivotcache for all the pivot tables. The first set of code is me testing working code in Excel. Now I want to translate this so that it runs from Access module.
Sub Update_PTSource()
With Sheets("Pivot")
.PivotTables(1).ChangePivotCache ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:=Sheets("Data").Range("data"))
End With
End Sub
Function pivot_refresh_test()
Dim pt As Variant
Dim wb, ws As Object
Dim strWBName As String
Dim strTabName As String
strWBName = "C:\Users\...\Packaged SKU.xlsb"
strTabName = "Pivot"
Set wb = GetObject(strWBName)
For Each pt In wb.Sheets(strTabName).Pivottables
pt.ChangePivotCache wb.PivotCaches.Create(SourceType:=xlDatabase, SourceData:=wb.Sheets("Data").Range("data"))
pt.RefreshTable
Next pt
End Function
I'm getting error on pt.ChangePivotCache line with error
invalid procedure call or argument
As far as I can see, you've only made one simple error:
xlDatabase is an Excel enum, and since you're using late bindings, you don't have access to it. You can use it's integer value, 1, instead:
For Each pt In wb.Sheets(strTabName).Pivottables
pt.ChangePivotCache wb.PivotCaches.Create(SourceType:=1, SourceData:=wb.Sheets("Data").Range("data"))
pt.RefreshTable
Next pt
I'm anal about using the least code possible when designing solutions so I found a much easier method to what I wanted to do.
My task was to update and refresh the source data of all my pivot tables. There was one source, multiple pivot tables. I created a dynamic named range for my source for this purpose in my template Excel book that Access was writing into. I never save over the template I just used saveas vba to create the filled copy.
However, since I had created a named range and am modifying always the same template file with Access, I don't need to write a program to change my pivotcache. I can just use 'Change Data Source' in Excel ribbon. The vba code gets much easier. It is just a variation of: ThisWorkbook.RefreshAll. No code loops required!
The whole power of my strategy is the formula that creates my named range. =Data!$A$5:OFFSET(Data!$A$5,COUNTA(Data!$A:$A)-1,52). You just set the first cell of your data, and set how many columns there are for your fields, the rows are automatically adjusted.
I hope this little rant help whoever navigates here. I've been searching a lot for good dynamic pivot table automation but most of the internet has crap info on this.

Excel 2016 VBA: Set Source Data for a Chart to a Named Table

The title is pretty self-explanatory.
I have a named table, Table_Unit_2_Data, which I would like to set as the source data for a chart that will be created using VBA.
During the recording of a macro I selected the entirety of the table, and inserted a chart. This is the code that I got (Build is the name of the Sheet):
Sub Test()
Range("Table_Unit_2_Data[#All]").Select
ActiveSheet.Shapes.AddChart2(240, xlXYScatterSmoothNoMarkers).Select
ActiveChart.SetSourceData Source:= Range("Build!$Y$1:$AD$2")
End Sub
Well, for one thing, as you can see, a specific $A$1 range is passed into the SetSoureData. This will not work because the range of Table_Unit_2_Data will change.
I attempted this:
With Sheet2.Shapes.AddChart2(240, xlXYScatterSmoothNoMarkers)
.Chart.SetSourceData (Sheet2.Range("Table_Unit_2_Data[#All]"))
End With
But then I get the error "Object Required".
I can't seem to phrase my search queries in such a way as to find relevant answers to this specific question on the internet so I apologize for asking what is likely a redundant question. If someone could help me with this problem I would be greatly appreciative and if anyone has a good article or source online for information regarding the nuances of chart creation within VBA that would also be very helpful.
Thank you.
In addition to what Domenic said (which is correct + would cause the "Object Required" error), your code doesn't make it clear what "Sheet2" is, except that it's the codename of some sheet. From the recorded macro, I can infer that the actual table is on a sheet called "Build", so another possibility to consider is that Sheet2 isn't actually the codename of Sheets("Build"). Again, I can't actually tell from the code provided.
While I do like using sheet codenames, I'd strongly recommend against using them if you're not going to make the names descriptive.
FWIW, there's another way to reference table ranges that's a little more flexible, especially if you're going to be referring to the table elsewhere in the code. Just make a ListObject variable:
Dim UnitTable2 As ListObject
Set UnitTable2 = Sheets("Build").ListObjects("Table_Unit_2_Data")
And you'll be able to reference any part of the table really easily:
Dim rng As Range
'Reference the whole table, including headers:
Set rng = UnitTable2.Range
'Reference just the table data (no headers):
Set rng = UnitTable2.DataBodyRange
'Reference just the data in a single column:
Set rng = UnitTable2.ListColumns("Col1").DataBodyRange
'Reference the headers only
Set rng = UnitTable2.HeaderRowRange

Refreshing Tables with VBA

I have two tables, one with a list of cities( we'll call this City List), and another with data points that correspond with those cities ( Call this The Data Table). The Data Table, is connected to a Select query that I built in MS SQL Server. This Select query/ Data Table has a single Where clause in which I have substituted the SQL criteria and replaced a ? in order to make it a parameter when connected to Excel.
Now that I have that out of the way, I'll explain what I'm trying to accomplish. I want to loop through the City List and for each city in the list, update The Data Table to reflect the data points for the city. Ultimately, I would like to loop through and each time The Data Table is refreshed, it saves a copy of the workbook with that specific table.
I have posted my current code down below, but my issue is that the table never refreshes with the current data that corresponds with the city that is currently selected via the loop. With that being said, if I hit the escape key to break out of the VBA macro, the table will then refresh with whatever the last city was before I stopped the macro.
Code:
Sub Macro1()
Dim WS As Worksheet
Dim WS2 As Worksheet
Dim CT As Variant
Dim MSG As String
Set WS = Sheets("Sheet1")
Set WS2 = Sheets("Sheet2")
CT = Range("A1").CurrentRegion
For i = 2 To UBound(CT, 1)
MSG = ""
For J = 1 To UBound(CT, 2)
WS.Range("$D$2").Value = CT(i, J) //Places the city into Cell $D$2 which is where The Data Table looks to for the parameter.
Exit For
Next J
ActiveWorkbook.Connections("Query from Database").Refresh
WS2.ListObjects(1).Refresh
Next i
End Sub
It's almost as though the macro is running too fast for the table to catch up and refresh. I've tried adding some wait times into the code, in hopes that it would give it enough time to allow the table to refresh, but that had no affect. I have also turned off Background Refresh, and that doesn't seem to do anything either. Right now it just loops through the city table, and with each city it shows that the query is refreshing, but after the query is finished refreshing, it goes onto the next city without ever updating The Data Table. HELP!
There are a couple of things I think you need to do -- maybe you've already done them.
When you set up your parameter/bind variable (which you have done), point it to a specific cell. Then, within your SQL Server query, make sure the parameter is bound to that range every time:
Forgive me if I'm overstating the obvious, but for those that don't know you get to this dialog by right-clicking the table and selecting Table->Parameters.
From there, as you iterate through your main table (the one with the cities in it), you can just take the value from each row in that table and update the cell with the binding parameter.
Something like this should work:
Sub RefreshAllCities()
Dim ws1, ws2 As Worksheet
Dim loCities, loDataTable As ListObject
Dim lr As ListRow
Set ws1 = ActiveWorkbook.Worksheets("Sheet1")
Set ws2 = ActiveWorkbook.Worksheets("Sheet2")
Set loCities = ws1.ListObjects("CityList")
Set loDataTable = ws2.ListObjects("DataTable")
' get rid of those pesky warnings to overwrite files
Application.DisplayAlerts = False
For Each lr In loCities.ListRows
ws2.Cells(1, 2).Value = lr.Range(1, 1).Value
loDataTable.QueryTable.Refresh BackgroundQuery:=False
ActiveWorkbook.SaveAs Filename:="C:\temp\" & lr.Range(1, 1).Value & ".xlsx", _
FileFormat:= xlOpenXMLWorkbook
Next lr
Application.DisplayAlerts = True
End Sub
I assume you wanted .xlsx files in this example. This will clobber any embedded VBA, which is actually a nice bonus, as the recipients of the filtered datasets won't have to be exposed to that. If you want xlsm or xlsb, that's easy enough to change.
By default Excel will "Enable Background Refresh" which will allow Excel to move on and continue execution before the query refresh is actually finished. Some people have been able to get it to work by calling .Refresh twice but it's pretty much random/arbitrary timing.
There should be an Excel option to uncheck in the Data Tables properties or you might be able to update the BackgroundQuery = False property from VBA through a reference to it
If you disable background refreshing then your code will sit and wait for the refresh to complete before moving on.

VBS file to refresh external data in specific work sheets

I want to create a .VBS file to refresh a table on a spcific worksheet. This table's data comes from an external data source (MS query). Usually, I just right click on the table and go to Refresh. This is what I would like to duplicate in the .VBS file. Is this possible?
I researched this on Stackoverflow (and Google), but I wasn't having luck finding code for the specific worksheet, just for the workbook. I'd like to do this for the specific sheet.
I did research how to access an Excel worksheet through .VBS, and this is what I have so far:
Dim objXLApp, objXLWb, objXLWs
Set objXLApp = CreateObject("Excel.Application")
Set objXLWb = objXLApp.Workbooks.Open("C:\Test\Test.xlsx")
Set objXLWs = objXLWb.Sheets(6)
' This is the code I added to solve my problem
For Each qry In objXLWs.QueryTables
qry.Refresh(false)
Next
objXLWs.Calculate
' End
objXLWb.Save
objXLWb.Close (False)
Set objXLWs = Nothing
Set objXLWb = Nothing
objXLApp.Quit
Set objXLApp = Nothing
MsgBox "Done"
Now, "objXLWs.Refresh" doesn't work. Neither does RefreshAll, RefreshAllData, etc. I get "Object does not support this property or method." I also tried .Calculate, but it doesn't work for this case. I am wondering if there is something that I am missing, or if I have to restructure this code a different way?
Any guidance would be appreciated.
Thanks,
Mark
Try something like
objXLWb.Connections("YourConnectionName").Refresh

Get Source Range of a PivotTable

I am building a set of reports to be updated daily for some users who are not super savvy when it comes to pivot charts and pivot tables in Excel (2010). I have data from an already automated data pull from our db, but to make that information useful (and more simple to use), I want to generate the pivots using VBA.
I have already generated the pivot tables just fine and can manually manipulate them to create the relevant charts. As I create the tables (all on one sheet), I am labeling them (if that helps solve my problem). Where I am running into trouble is creating the pivot charts, and specifically, setting the data source.
Since the charts are based on pivots that may change in size depending on the data present, I can't just assign a fixed range as the source. Same problem with a named range, as I still don't know what range to assign a name, unless I can assign a PT as a range.
I've tried something like this, but that didn't work, since I'm not looking at an actual 'Range' object:
Dim MyChartObject As ChartObject
Dim MyChart As Chart
'These variables are assigned elsewhere in the code.
Set MyChartObject = wksMainCharts.ChartObjects.Add( _
iChartLeft, iChartTop, iChartWidth, iChartHeight)
Set MyChart = MyChartObject.Chart
MyChart.SetSourceData wksMainPivot.PivotTables(PivotName).SourceData 'Fails; requires Range object
'More code to follow...
I do know that this is a string representation of the wrong range, but I was playing with what I could figure on my own.
Essentially what I want is the opposite of this question, which seeks to find the pivot table at a specified range. I also reviewed this question, but it's not quite what I'm looking for (there are also no answers provided).
Also: I'm asking here for a way to get the range of a PT, which I think would be interesting to know, but ultimately, I just want to create the pivot chart, so if anyone wants to offer a better method than what I've started on, I'm completely open to that as well.
Is this what you are trying to achieve?
Sub Sample()
Dim Chrt As Chart, pvtTbl As PivotTable
Set pvtTbl = ActiveSheet.PivotTables(1)
Set Chrt = ActiveSheet.ChartObjects(1).Chart
Chrt.SetSourceData Source:=pvtTbl.TableRange1
End Sub
So your this line
MyChart.SetSourceData wksMainPivot.PivotTables(PivotName).SourceData
becomes
MyChart.SetSourceData wksMainPivot.PivotTables(PivotName).TableRange1