Auto-updating Power Query Connection via VBA - excel

I have a Power Query set in myexcel.xlsx. I set its connections's properties as
this and this.
I wrote a VBA code like the following
Sub UpdateData()
Dim filename As String
Dim wbResults As Workbook
filename = "C:\myexcel.xlsx"
Set wbResults = Workbooks.Open(filename)
ActiveWorkbook.RefreshAll
wbResults.Close savechanges:=True
End Sub
When I open the myexcel.xslx manually, the Power Query connection updates. But through VBA code it doesn't. I should add I tested this with an old fashioned Excel Connection andit works fine through VBA code. But the problem is with Power Query connections. Any thoughts?

It is actually rather easy, if you check out your existing connections, you can see how the power query connection name starts, they're all the same in the sense that they start with "Query - " and then the name... In my project, I've written this code which works:
Sub RefreshQuery()
Dim con As WorkbookConnection
Dim Cname As String
For Each con In ActiveWorkbook.Connections
If Left(con.name, 8) = "Query - " Then
Cname = con.name
With ActiveWorkbook.Connections(Cname).OLEDBConnection
.BackgroundQuery = False 'or true, up to you
.Refresh
End With
End If
Next
End Sub
This will refresh all your power queries, but in the code you can see it says:
If Left(con.name, 8) = "Query - " Then
This just means if the name of the connection, the first EIGHT characters starting from the LEFT and moving towards the RIGHT (the first 8 characters) equals the string "Query - " then...
and if you know the name of your query, adjust the 8 to a number that will indicate the amount of characters in your query name, and then make the statement equal to your query connection name, instead of the start of all power query connections ("Query - ")...
I'd advise NEVER updating all power queries at once IF you have a large amount of them. Your computer will probably crash, and your excel may not have auto saved.
Happy coding :)

If you refresh all connections via a loop, you cannot control the order in which this happens. If you need control of the sequence, or if you need to refresh just a couple of Power Queries, this is also an option:
The first function refreshes one single Power Query. The argument of the function in parentheses is the name of the query as visible on the "Queries and connections" pane in Excel. Note how this is translated into the connection name by adding "Query - " as prefix.
The second function then uses the first function to call specific Power Queries in a specific order, giving you full control.
Public Sub RefreshSpecificPowerQuery(pqName As String)
Dim con As WorkbookConnection
Dim conName As String
conName = "Query - " & pqName
With ActiveWorkbook.Connections(conName).OLEDBConnection
.BackgroundQuery = False 'or TRUE, as the case requires
.Refresh
End With
End Sub
Public Sub RefreshListOfPowerQueries()
Call RefreshSpecificPowerQuery("pqMyFirstPowerQueryName")
Call RefreshSpecificPowerQuery("pqMySecondPowerQueryName")
End Sub

Since you're using Power Query, which is different to Power Pivot, you have two options:
Automatic Update the data source when the file is open - (http://www.excel2013.info/power-query/automatic-update/)
Write a VBA script for updating it
For Each cn In ThisWorkbook.Connections
If cn = "Power Query – Employee" Then cn.Refresh
Next cn
End Sub
copied from here:
https://devinknightsql.com/category/power-query/

Just in response to James Heffer’s post which worked for me after some tweaking.
If you live in non-English speaking country your connection changes name.
You can see connection name by adding a Debug.Print command like here
Sub RefreshQuery()
Dim con As WorkbookConnection
Dim Cname As String
For Each con In ActiveWorkbook.Connections
Debug.Print con
If Left(con.name, 8) = "Query - " Then
Cname = con.name
With ActiveWorkbook.Connections(Cname).OLEDBConnection
.BackgroundQuery = False 'or true, up to you
.Refresh
End With
End If
Next
End Sub
After you run the code, it will show you the localized names. Mine is called “Forespørgsel – LevBonusData”
Hope it helps somebody 😊

You can try this code as well
Sub auto_open()
ActiveWorkbook.RefreshAll
Selection.ListObject.QueryTable.Refresh BackgroundQuery:=False
ThisWorkbook.Save
ChDir "D:\Data"
ActiveWorkbook.SaveAs Filename:="D:\Data\abc.txt", FileFormat:=xlText, CreateBackup:=False
Application.Quit
End Sub
When you will open file at that time macro will run automatically and also data will be saved and in last file will be saved as TXT format as well :)

Related

Opening specific Access form from a cell in excel

I have been researching this and cant seem to find a solution that works. I am attempting to have a button in excel that when clicked opens access and opens a specific form. We keep all our machine setup sheets as forms in access.
How our operators use it:
When you want to find your form to print out to take to the machine, you open the access db and it automatically opens the form. You then hit "Toggle Filter" and then filter to the part number you are working with. They then print out that form.
How I want to use it:
I have a cell in excel with a part number. When a command button is clicked, it opens up the access db and filters for the specific form I want to view.
My current code is below:
Sub DisplayForm()
Dim StrDbpath As String
Dim PartNum As String
PartNum = Worksheets("CA Info Search").Range("A2").Value
StrDbpath = "F:\MACHSET\Setup Files.mdb"
Set appAccess = CreateObject("Access.Application")
With appAccess
.OpenCurrentDatabase StrDbpath
.Visible = True
.DoCmd.OpenForm "Machine Setup Sheet"
With .Forms("Machine Setup Sheet")
.Filter = "[PART #:]='" & PartNum & "'"
.FilterOn = True
End With
End With
End Sub
EX. Part number I am testing with is 350-0158-000
When this is ran, the db opens and prompts me to enter parameter value for Part #:. I just hit enter and it takes me to a blank screen. If i double check the advanced filter to see what is being filtered, it says I am filtering Part #: by '350-0158-000' but the form is still blank
[1][Parameter Prompt]
[1]: https://i.stack.imgur.com/6dEuc.png
[2][Blank screen after I hit enter]
[2]: https://i.stack.imgur.com/dexzK.png
[3][Advanced Filter]
[3]: https://i.stack.imgur.com/6eUsX.png
[4][Advanced filter showing filter value]
[4]: https://i.stack.imgur.com/LnXN2.png
Too much to put in a comment.
To continue from my last comment, you don't need to open the form again, just apply the filter. Try the below (I've no means to test it).
'...
Set appAccess = CreateObject("Access.Application")
With appAccess
.OpenCurrentDatabase strDbPath
.Visible = True
.DoCmd.OpenForm "Machine Setup Sheet"
With .Forms("Machine Setup Sheet")
.Filter = "[PART #:]='" & PartNum & "'"
.FilterOn = True
End With
End With
Setting the .FilterOn = True property, will reset and reapply the filter.
weird bug that goes away. the problems occur because the spaces in the form name and the with statement, but once I fixed those problems, I could put the spaces and with statement back. Hence, I couldn't verify the answer.
assumptions you have a worksheet named CA Info Search. A2 is formatted general and that cell shows 350-0158-000. Also form is named Machine Setup Sheet and actually has a column named Part #:
try this code:
Sub DisplayForm()
Dim StrDbpath As String
Dim PartNum As String
PartNum = Worksheets("CA Info Search").Range("A2").Value
StrDbpath = "F:\MACHSET\Setup Files.mdb"
Set appaccess = CreateObject("Access.Application")
appaccess.OpenCurrentDatabase StrDbpath
appaccess.docmd.OpenForm "Machine Setup Sheet"
appaccess.Forms("Machine Setup Sheet").Filter = "[PART #:]='" & PartNum & "'" 'need the apostrophes to indicate 350-0158-000 is a string
appaccess.Forms("Machine Setup Sheet").FilterOn = True
appaccess.Visible = True
End Sub
If the above code doesn't work, then remove the spaces from the form name and adjust the code accordingly. Replace all instances "Machine Setup Sheet" with "MachineSetupSheet". Then if the code works and you want the with statement and "Machine Setup Sheet", try adding them back in.
Solution worked in Access and Excel 2016

VBA - Remove Password from OLEDB Connection String

I have an excel file that contains a series of OLEDB connections leveraged by several pivot tables. I would like to create a VBA function that removes all password from that several connection string as the file is closed(so that the users password will not be persisted). First I thought all I need to do was set the "Save Password" property to false, something like this:
Public Sub RemovePasswordByNamePrefix()
Dim cn As Object
Dim oledbCn As OLEDBConnection
For Each cn In ThisWorkbook.connections
Set oledbCn = cn.OLEDBConnection
oledbCn.SavePassword = False
Next
End Sub
Should work right, on closing the file and reopening it you shouldn't see the password anymore in the connection string. It should not be "Saved":
Wrong, password is still there... It has been "Saved". Not sure what that feature is supposed to do. Maybe there referring to a different password? So, I attempted the big hammer approach, unfortunately it has it's own challenges, and so far I haven't gotten that working.
I'm not quite sure how to do this... Why is this so massively insecure? It persists plaintext passwords every file that contains a connection string of this sort, easily readable by whoever could access that file.
Maybe I could make some sort of Regex to remove just the password from the file? When I do that in the interface my cubes refresh and prompt me for my credentials, (I wonder)would that occur if I did it in VBA, even if the trigger is upon excels closure?
Bottom Line: What is the best way to prevent these passwords from being persisted in the file upon it's closure?
#TomJohnRiddle points out that I should look at modifying the connection string similar to the following question. Initially I was concerned that taking this approach could prompt the user with a login screen after modifying the connection string. However since I don't have any better ideas I gave it a shot, and it seems to work, here's what I've mocked up:
Public Sub RemovePasswordByNamePrefix()
Dim cn As Object
Dim oledbCn As OLEDBConnection
Dim regEx As New RegExp
regEx.Pattern = "Password=[^;]*;"
For Each cn In ThisWorkbook.connections
Set oledbCn = cn.OLEDBConnection
oledbCn.SavePassword = False
oledbCn.connection = regEx.Replace(oledbCn.connection, "")
oledbCn.CommandText = "" 'My app repopulates this after open
Next
End Sub
and it seems to work:
So I think I'll go with this approach, but I'm still open to other suggestions. Would be nice to clear everything and fully reload it, but so far that doesn't appear to be possible.
I'm also concerned with what versions of VBA support the "Regex" references. I would like something that would be Excel 2010+ 32/64 bit compatible. I have yet to test this on any older version(I'm currently running Office 365). I assume it will all work fine, but I've been unpleasantly surprised with these things in the past.
See this on SQL Server authentication Authentication in SQL Server. There it says you can use 100% Windows Authentication or you can use Mixed-Mode (Windows Authentication and passwords). If you really want to banish passwords from connection strings do not install with Mixed Mode Authentication just run 100% Windows Authentication. However, there may be some code already deployed written to use passwords so that may not always be practical.
So, the other way to discipline no passwords is to use
Integrated Security=true;
in your connection strings. This Stack Overflow question on the subject is well visited.
#NigelHeffernan suggests a slightly different approach for how to do this, here's a version without regex's:
Public Sub RemovePasswordByNamePrefix()
Dim cn As Object
Dim oledbCn As OLEDBConnection
Dim stringArray
Dim stringElement As Variant
Dim newStringArray As Variant
For Each cn In ThisWorkbook.connections
Set oledbCn = cn.OLEDBConnection
oledbCn.SavePassword = False
stringArray = Split(oledbCn.connection, ";")
For Each stringElement In stringArray
If Not InStr(stringElement, "Password=") Then
If IsEmpty(newStringArray) Then
newStringArray = Array(stringElement)
Else
ReDim Preserve newStringArray(UBound(newStringArray) + 1)
newStringArray(UBound(newStringArray)) = stringElement
End If
End If
Next
oledbCn.connection = Join(newStringArray, ";")
oledbCn.CommandText = "" 'My app repopulates this after open
Next
End Sub
I'm not sure the benefit of this method(other than a lack of another library reference) and I haven't tested outside of one connection string/one machine yet. My connection strings don't contain the "Extended Properties" field, maybe this approach wouldn't work for that.
It looks like you are using DSNs, which is something that Excel will create if you use the default connection management tools in the GUI. When working with DSNs, the ODBC driver will sometimes put cleartext passwords in to the Registry, even when you don't select "Save Password".
Instead of allowing Excel to manage your connections you would need to manage them yourself. Here is some example code from MS MVP Ben Clothier. You would have to modify the connection string to match your use case. You might be able to copy the details from your existing connections before you remove them.
Public Function InitConnect(UserName As String, Password As String) As Boolean
‘ Description: Should be called in the application’s startup
‘ to ensure that Access has a cached connection
‘ for all other ODBC objects’ use.
On Error GoTo ErrHandler
Dim dbCurrent As DAO.Database
Dim qdf As DAO.QueryDef
Dim rst As DAO.Recordset
‘<configuration specific to MySQL ODBC driver>
strConnection = “ODBC;DRIVER={MySQL ODBC 5.1 Driver};” & _
“Server=” & ServerAddress & “;” & _
“Port=” & PortNum & “;” & _
“Option=” & Opt & “;” & _ ‘MySql-specific configuration
“Stmt=;” & _
“Database=” & DbName & “;”
Set dbCurrent = DBEngine(0)(0)
Set qdf = dbCurrent.CreateQueryDef(“”)
With qdf
.Connect = strConnection & _
“Uid=” & UserName & “;” & _
“Pwd=” & Password
.SQL = “SELECT CURRENT_USER();”
Set rst = .OpenRecordset(dbOpenSnapshot, dbSQLPassThrough)
End With
InitConnect = True
ExitProcedure:
On Error Resume Next
Set rst = Nothing
Set qdf = Nothing
Set dbCurrent = Nothing
Exit Function
ErrHandler:
InitConnect = False
MsgBox Err.Description & ” (” & Err.Number & “) encountered”, _
vbOKOnly + vbCritical, “InitConnect”
Resume ExitProcedure
Resume
End Function
NOTE:
This is written for MS Access, not Excel. The concepts are all the same. You might want to try making your front end in Access and then export your views to Excel from Access. This would allow you better control of the link to your back-end and allow you to use SQL in Access to define what you want to export to Excel.
READ THIS:
https://www.microsoft.com/en-us/microsoft-365/blog/2011/04/08/power-tip-improve-the-security-of-database-connections/

Query Pipe Delimited File Not Returning all Records

I am querying a pipe delimited text file using VBA with ADODB. I have tried using both the ACE and JET engines, but the results are the same, and not correct. I saved a "schema.ini" file in the directory telling the engine that the file is pipe delimited. When the query finishes though, I have data that shifted columns and other fields that are blank when I can see the data in the text file. I use the CopyFromRecordset method to transfer the results to my workbook. Can anyone see something off in my code? Or has anyone run into this?
My connection string:
With Conn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & strPath & ";" & _
"Extended Properties=""text;HDR=YES;FMT=Delimited"""
.Open
End With
Value of "strPath":
strPath = "H:\Folder\"
SQL String:
strSQL = "SELECT * FROM " & strFile & ";"
Data transfer:
With ThisWorkbook
With .Sheets("CashFlows")
.Range("a2").CopyFromRecordset Rst
With .Range("a1")
For i = 0 To Rst.Fields.Count - 1
.Offset(0, i) = Rst.Fields(i).Name
Next i
End With
End With
End With
I have never had issues with ADO (either Ace or Jet) and I use them quite frequently. I have not, however, used them for pipe delimited files before.
Please leave a comment if I am unclear in my explanation, or if you require more code.
EDIT
See the two lines out of the pipe delinted text file below. The first line parses normally. The second drops the "651111100" and "654444475". The strangest thing is that these parse totally fine when I open them in Excel directly and use text to columns.
08/31/2015|000|000|Recital #5546|0000000012|88885463|123334563
08/31/2015|000|000|DII #7412|651111100|654444475|00000326541
I re-checked, and the issue is only that it is dropping columns, not that it is shifting them around. Sorry for any confusion.
UPDATE
I did a find/replace in the text file and put in "^" instead of "|". I then tried to run this again and I ran into the same issue!
While I am still not sure what the problem might, the following is a usable solution:
Option Explicit
Public Sub ImportTextFile()
Dim lngRowNumber As Long
Dim strImportFile As String
Dim intNextFreeFile As Integer
Dim strOneImportLine As String
Dim arySplitLine As Variant
With Application
.ScreenUpdating = False
.Calculation = xlManual
.EnableEvents = False
End With
intNextFreeFile = FreeFile
strImportFile = "C:\path\to\the\TextFile.csv"
lngRowNumber = 1
Open strImportFile For Input Access Read As #intNextFreeFile
Do While Not EOF(intNextFreeFile)
Line Input #intNextFreeFile, strOneImportLine
arySplitLine = Split(strOneImportLine, "|")
Sheets(1).Cells(lngRowNumber, 1).Value2 = arySplitLine
lngRowNumber = lngRowNumber + 1
Loop
Close #intNextFreeFile
With Application
.ScreenUpdating = True
.Calculation = xlAutomatic
.EnableEvents = True
End With
End Sub
The test-run on my computer completed the import for one million rows in a 80MB file within 12 seconds.
While I am sure that there are better ways to do this which outperform this solution you might want to give it a try in the meantime.

Keep Excel sub runing after calling access sub procedure

within my Excel sub I am calling an Access sub procedure that runs in the background (DB does not open) and updates a table in my DB. Everything works perfect, except that my Excel Sub will not proceed to the next line of code until Access is done running it's sub I called from Excel. So, my question is this...is there any way to call/run an Access Macro/Sub procedure from within an Excel Sub and have Excel proceed through the rest of the code and not have to wait for the Access Macro/sub to finish in order to proceed? Code: below:
Set acObj = CreateObject("Access.Application")
acObj.Application.Visible = False
acObj.OpenCurrentDatabase "C:\Intraday Data\Intraday.accdb"
acObj.Application.Run "RunData"
MsgBox "Done!"
So Basically I want to get the done prompt right away w/o having to wait 30 seconds for the access procedure to finish...anyone have any insight on this they can share w/ me please?
Thanks!
Here's some simple code to demonstrate an asynchronous call to an Access procedure:
Sub ExecuteAccessActionQuery()
' Sample demonstrating how to execute an action query in an Access accdb asynchronously
' Requires a reference to a Microsoft ActiveX Data Objects library
Dim cn As ADODB.Connection
Dim strQuery As String
Dim strPathToDB As String
Dim dTimer As Double
' Change path and query name as necessary
strPathToDB = "C:\some path\database name.accdb"
strQuery = "qmtTempTable"
Set cn = New ADODB.Connection
With cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & strPathToDB & ";"
.Open
End With
dTimer = Timer
cn.Execute strQuery, , adCmdStoredProc + adAsyncExecute
MsgBox Timer - dTimer
Set cn = Nothing
End Sub
You can emulate an asynchronous call in VBA by creating a trigger in the client application which will then execute the required subroutine. This technique is described well here (note the 1 second time delay required a few posts down):
http://social.msdn.microsoft.com/Forums/en-US/0546f8eb-d786-4037-906e-1ee5d42e7484/asynchronous-applicationrun-call?forum=isvvba
Currently, you are manipulating data in the Access tables without initiating the application, so you can either use a new instance of Excel in which to create the trigger, or you can open the Access application and do it there (http://msdn.microsoft.com/en-us/library/office/aa213969%28v=office.11%29.aspx). Either way, you must have another independent application (process) within which to execute your 'asynchronous' routine.

Access - Excel Integration

Hey all, have been working on designing a new database for work. They have been using Excel for their daily reports and all the data is stored in there, so I decided to have the back-end of the database in Access and the front-end in Excel, so any analytical work can be easily performed once all the data has been imported into Excel.
Now I'm fairly new to VBA, slowly getting used to using it, have written some code to transfer one of the calculated tables from Access to Excel:
Option Explicit
Public Const DataLocation As String = "C:\Documents and Settings\Alice\Desktop\Database\TestDatabase21.accdb"
Sub Market_Update()
Call ImportFromAccessTable(DataLocation, "Final_Table", Worksheets(2).Range("A5"))
End Sub
Sub ImportFromAccessTable(DBFullName As String, TableName As String, TargetRange As Range)
Dim cn As ADODB.Connection, rs As ADODB.Recordset, intColIndex As Integer
Set TargetRange = TargetRange.Cells(1, 1)
' open the database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & DBFullName & ";"
Set rs = New ADODB.Recordset
With rs
' open the recordset
' .Open TableName, cn, adOpenStatic, adLockOptimistic, adCmdTable
' all records
.Open "SELECT * FROM Final_Table", cn, , , adCmdText
' filter records
For intColIndex = 0 To rs.Fields.count - 1 ' the field names
TargetRange.Offset(0, intColIndex).Value = rs.Fields(intColIndex).Name
Next
TargetRange.Offset(1, 0).CopyFromRecordset rs ' the recordset data
End With
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
Sub Company_Information()
Dim companyName As String
On Error GoTo gotoError
companyName = Application.InputBox(Prompt:="Enter Company Name", _
Title:="Company Name", Type:=2)
Exit Sub 'Don't execute errorhandler at end of routine
gotoError:
MsgBox "An error has occurred"
End Sub
The above code works fine and pulls up the desired calculated table and places it in the right cells in Excel.
I've got two problems that I'm having trouble with; firstly I have some cell-formatting already done for the cells where the data is going to be pasted into in Excel; I want it to apply the formatting to the values as soon as they are pasted in Excel.
Secondly; I have an add-on for Excel which updates some daily Stock Market values; these values need to be transferred into Access at the end of each working day, to keep the database maintained, I tried some code but have been having some problems with it running.
The code for this part can be seen following:
Sub UPDATE()
Dim cnt As ADODB.Connection
Dim stSQL As String, stCon As String, DataLocation As String
Dim stSQL2 As String
'database path - currently same as this workbook
DataLocation = ThisWorkbook.Path & DataLocation
stCon = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & DataLocation & ";"
'SQL code for GL Insert to Access
stSQL = "INSERT INTO Historical_Stock_Data SELECT * FROM [Portfolio] IN '" _
& ThisWorkbook.FullName & "' 'Excel 8.0;'"
'set connection variable
Set cnt = New ADODB.Connection
'open connection to Access db and run the SQL
With cnt
.Open stCon
.CursorLocation = adUseServer
.Execute (stSQL)
End With
'close connection
cnt.Close
'release object from memory
Set cnt = Nothing
End Sub
I get the following error with this.
Run-time Error '-2147467259 (80004005)'
The Microsoft Jet database engine cannot open the file 'Cocuments and Settings\Alice\Desktop\Database'. It is already opened exclusively by another user or you need permission to view its data.
I'm fairly new to databases, VBA and Access so any help would be greatly appreciated.
Also I have been told that the above method of having an Excel front-end and Access back-end is not recommended but alot of the analysis they conduct is done through Excel, and the charts feature in Excel is much better than Access in my experience atleast; and that is also one of the requirements for this project.
Thank you advance!
Solution to your first problem:
Sorry to be the bearer of bad news, but your entire first module is unnecessary. Instead, try:
Go to Data->Import External Data->Import Data, select your Access file, select your table, and presto! done!
Right-click on your new "External Data Range" to see a number of options, some related to formatting. You can even keep the original cell formatting and just update the values. I do this all the time.
To update the Excel data table later, there is a "External Data Range" toolbar that allows you to refresh it as well as a "refresh all" option to refresh every table in the Excel file. (You can also automate this thru code. It'll take some trial and error, but you're definitely up to the task)
Regarding your second problem
I've never used it, but there is also a "New Web Query" option in there as well. I assume it can be manipulated and updated the same way.
And lastly
Your choice of the Excel front-end and the Access back-end sounds good for your needs. It gets the data to your analysts in a medium they are familiar with (Excel) while keeping the calculations out of the way in Access. Technically, you could try putting all your calculations in Excel, but that might the Excel file much bigger and slower to open.
Do the data entry/updating/reviewing in Access. One of Access' strengths is using forms that allow you to update the tables without any code. Then allow the users to easily export the data to Excel such as by clicking on some command buttons.
Modules: Sample Excel Automation - cell by cell which is slow
Modules: Transferring Records to Excel with Automation
nothing wrong in principle with the excel/access pairing. I'm not familiar with ADO (I use DAO), but your error message seems to be indicating that the path to the datasource is not fully formed; or you already have it opened and hence are locking it.

Resources