Returning a web query can be very handy. Unfortunately if you want to run any code when this refreshes, there’s no native way to do this. We can, however, create an event for this with a few short steps.
This will all be done inside the Visual Basic Editor (VBE). To get there click Visual Basic on the Developer tab (pictured below) or hit ALT + F11.
Step 1: Create a class module
In the VBE, right-click your project and select Insert
Class Module (pictured below). You can also do this from the Insert menu on the command bar.
Step 2: Rename class module
This is a very important step and is probably the most missed step when people re-create class module solutions.
For this example we will rename it (select the class module, press F4) to “clsQuery“.
The class name is referred to in code later on in this post. If you name it something different, be sure to adjust your code as well.
Paste the following code into the class module (double-click it in the PE to see the code pane):
[sourcecode language=”vb”]Option Explicit
Public WithEvents MyQuery As QueryTable
Private Sub MyQuery_AfterRefresh(ByVal Success As Boolean)
If Success Then MsgBox “Query has been refreshed.”
End Sub
Private Sub MyQuery_BeforeRefresh(Cancel As Boolean)
If MsgBox(“Refresh query?”, vbYesNo) = vbNo Then Cancel = True
End Sub[/sourcecode]
Your code should now look like the following:
Step 3: Create the standard module
Right-click your project in the PE and select Insert
Module.
Paste the following code (updated on 10-Feb-14, thanks to Andy Pope for pointing out a flaw!):
[sourcecode language=”vb”]Option Explicit
Dim colQueries As New Collection
Sub InitializeQueries()
Dim clsQ As clsQuery
Dim WS As Worksheet
Dim QT As QueryTable
For Each WS In ThisWorkbook.Worksheets
For Each QT In WS.QueryTables
Set clsQ = New clsQuery
Set clsQ.MyQuery = QT
colQueries.Add clsQ
Next QT
Next WS
End Sub[/sourcecode]
To initialize this code when the workbook opens, double click ThisWorkbook module and use the following workbook open code:
[sourcecode language=”vb”]Option Explicit
Private Sub Workbook_Open()
Call InitializeQueries
End Sub[/sourcecode]
That’s it, you now have query before/after events you can tie into. Note that if you make changes to the class module you’ll have to run the initialize code again.
Thank you ExcelAndAccess for another great educational post. Nice use of graphics.
Great post ExcelAndAccess! Always enjoy the chance to learn how to use a class module. What are some examples of data you can query from the web?
Well you could really query anything you want actually! A straight-up web query will need a URL. Once you get that it’ll be previewed in the dialog box and waiting for you to tell it which part of the web page you’re wanting to return for your query. So basically any valid URL on the internet will work!
Another great tool to do web querying from is Power Query. Based on the language M, it’s a super powerful tool which lets you get data from many sources – the web being one of them. One of the things I did when I first got the tool was check my Facebook and Twitter accounts for scraping the data into Excel. Not too meaningful, but really fun to do!
One thing I’m working on now for someone is to query all of the snowfall data for specific monitored sites. As always there’s 101 ways to skin this cat, and this is just one of them, but it’s a good example of getting current information from the web into Excel. In this specific case we used an after query update event to parse out specific data for summarization.
Thanks, but it doesn’t work for me. My data connection is as follows : I use “Existing Connections” to set up a table. A right-click on the table and “Edit Query” brings up the Connection and Command Type/Text for my SQL “EXEC spMyQuery”….. but adding your code has made no difference. The initialising is being called, but Data..Refresh All doesn’t do anything different to what it used to. I’m still looking for an event I can hook into to run code after this refresh! Thanks.
.. the For Each (For Each QT In WS.QueryTables) doesn’t loop over anything for me. My data connection method doesn’t seem to appear in ThisWorkbook.Worksheets.QueryTables ?
That may be because your data connection is member of ListObject instead of Worksheet, which is usually the case if you create your connection in Excel 2010 – 2013.
Change the example code as follows to include QueryTables under ListObjects:
Sub InitializeQueries()
Dim clsQ As clsQuery
Dim WS As Worksheet
Dim QT As QueryTable
Dim LO As ListObject
For Each WS In ThisWorkbook.Worksheets
For Each QT In WS.QueryTables
Set clsQ = New clsQuery
Set clsQ.MyQuery = QT
colQueries.Add clsQ
Next QT
For Each LO In WS.ListObjects
Set QT = LO.QueryTable
Set clsQ = New clsQuery
Set clsQ.MyQuery = QT
colQueries.Add clsQ
Next LO
Next WS
End Sub
This is exactly what i needed to add to get it to work in Excel 2016. Cheers!
Doesn’t work. Event doesn’t fire.
Next……..
Great routine, exactly what I was looking for, works well, thank you very much for making it available.
Because I added the code to an existing refresh routine, it lets the Querytable refresh routine run to completion & then a few seconds later the messagebox displays informing me of success. My burning question is: How does your code work?
Thanks
thanks very much for this code. It is the best I have seen on this subject and very helpful.
Tried to use this code. Working in excel 2010. but syntax errors coming up for the lines:-
If Success Then MsgBox “Query has been refreshed.”
and
If MsgBox(“Refresh query?”, vbYesNo) = vbNo Then Cancel = True
Editor doesn’t like the text in quotations for some reason.
Any idea why?
You can get the code to work for you by asking our Excel experts on VBAExpress.com. Free, no charge, they will happily assist you.
Here is the link to the Excel forum.
There is a forum per application.
i also have similar problem can you send me the link
Had the same issue, replacing the curly quotes with straight ones sorted it.
Thanks for this — it made one thing in my life a bit less aggravating, which is a very happy-making thing, indeed!
Excel is fun 😉 We are happy we were able to bring you some peace.
Just tried your code, and it seems to work fine except that the AfterRefresh does not run if the query had no errors. Just if it had errors the afterrefresh event is called.
AfterRefresh runs when BackgroundQuery:=False
qt.Refresh BackgroundQuery:=False
So, if I had a macro that ran some events, how do I utilize what you provided in my macro. for example: after the data is refreshed utilize this macro to hide sheets on the workbook.
Sub hidesheets()
Dim WS As Variant
Sheet1.Visible = xlSheetVisible
For Each WS In Array(“sheet2”, “sheet3”, “sheet4”)
Sheets(WS).Visible = Not Sheets(WS).Visible
Next
End Sub
Your original code is incorrect – so you might want to fix it – you are only creating one class and then assigning it multiple times – rather than create a instance of the class for each query.
I’m using the code including QueryTables under ListObjects (Excel 2010), because I have a web query and three SQL queries. Works fine! Now I want to deploy different code for the queries during the MyQuery BeforeRefresh and AfterRefresh events; thus my question is how to determine which query belongs to which event. I have tried referring to MyQuery.Name, but this only returns the name of the web query.
Please, can you help me with this issue?
Kind regards, Tom
This is AWESOME!!!!!!!!! solved so many problems!!!!
Our pleasure. You can always contact us directly for help.