Run query from VB in MS Access - sql

I'm trying to set the sql code for a query and then run the query from VB. The problem is that when I change the sql dynamically, the VB opens the query but does not refresh it. It still shows results from old sql. If I check the sql, it has changed, and if I then run the query (! button), it runs with the new sql.
I'm doing:
Set qdf = CurrentDb.QueryDefs("temp_query")
qdf.SQL = SQL_query_string
MsgBox (qdf.SQL)
DoCmd.OpenQuery ("temp_query")
With SQL_query_string containing the new, dynamically generated sql. The message box shows me that I indeed have the new string. And, as I said, I can check it in the query itself, and it has changed, but DoCmd.OpenQuery("temp_query") seems to just give the query the focus, not actually running it. What command runs it with the fresh sql?

If the query is already open, you need to close it and then reopen, interestingly enough, you probably do not even need to check if it is open, you can just close and then run the code.
DoCmd.Close acQuery, "temp_query"
Set qdf = CurrentDb.QueryDefs("temp_query")
qdf.SQL = SQL_query_string
'MsgBox (qdf.SQL)
DoCmd.OpenQuery "temp_query"
Make sure you have not used Set Warnings or On Error resume next, because they will mask errors.

Related

CurrentDb.Execute Error 3061 and DAO.Recordset Error

On a form, a user is able to select a value of either 1 or 2. This number is on an unbound control called CountVal.
When the user selects the submit button, an update query is ran. The following is the query.
UPDATE UserData_T SET UserQuantity = Forms!MainUser_F!CountVal.value;
The query, when run separately, runs as should. The issue comes when I call it in the submit button click event. I use the CurrentDb.Execute method. This method throws a
3061 error of "Too few parameters'.
I have found through much research that control based parameters do not work with the execute method. It works when I set the value as either 1 or 2 but not through the control. I need to have this dynamic.
I have also tried the DAO.Database.OpenRecordset() method, however I am struggling with looping through each record. Logically, I would gravitate to a for each but I'm not finding any references on using this loop with a DAO recordset. Also, wouldn't looping through the recordset be extremely slow? Could I also add an if statement to the query itself?
In MS Access, stored queries can refer to open form or report controls as parameters. However, queries called via DAO methods like CurrentDb.Execute, do not see the GUI interface and hence cannot evaluate form or report controls.
So to continue using form controls as is, simply save your SQL statement as a stored query and call it with DoCmd.OpenQuery (which do not need to be closed for action queries like UPDATE, INSERT, DELETE). This is the coding counterpart to clicking the stored query via Navigation Pane.
DoCmd.OpenQuery "mySavedUpdateQuery"
Do note the above will raise prompts of data changes. To suppress such prompts, use DoCmd.SetWarnings:
DoCmd.SetWarnings False
DoCmd.OpenQuery "mySavedUpdateQuery"
DoCmd.SetWarnings True
You must concatenate the value to build the SQL:
Dim Sql As String
Sql = "UPDATE UserData_T SET UserQuantity = " & Forms!MainUser_F!CountVal.Value & ""

Can I run a VBA macro to run SQL queries I have saved in a MS Access database?

I spent alot of time to create 24 different separate MS Access queries that I have saved in the MS Access database. They have to be run in a specific order.
I want to run a macro that will just execute them one after another.
I know I can do this using VBA but I have copy all the SQL into VBA one at a time.
is there a way I can just have VBA run the saved queries in the MS Access database without copying them into VBA?
I am very good with SQL. 15+ years doing Oracle work, Code writing, DB Tuning, Data DBA work.
I am new to using SQL and VBA to run against MS Access DB.
To clear a few terminologies:
MS Access GUI comprises of five main object types: tables, queries, forms, macros, and modules. Technically, there is no such thing as VBA macros in MS Access. This is a terminology from MS Excel where each subroutine is defined as a macro.
In MS Access there is a physical object known as a macro which is divorced from VBA (though can call VBA code). Coded routines which includes all functions and subroutines in MS Access are encapsulated in the module (object) usually behind forms, reports, or standalone modules.
Do remember each Microsoft Office application maintains its own object library and should not be conflated with one another. VBA itself is a separate component installed by default (see Tools \ References in IDE). Any language (Java, PHP, Python, etc.) that can run COM-interfacing can connect and run each Office app's object library. VBA is simply one type that does this.
MS Access maintains stored queries (as another object) which can be actions queries (UPDATE, INSERT even CREATE TABLE or ALTER TABLE) or recordset queries (SELECT). You do not have to copy all of SQL code inside VBA to run these saved query objects. In fact, stored queries run more efficiently than string queries called in code since the MS Access engine compiles the best execution plan with stored queries.
Having said that, consider a macro calling multiple OpenQuery actions or coded subroutine inside a module calling multiple DoCmd.OpenQuery commands
Macro
OpenQuery
QueryName: myfirstActionQuery
View: Datasheet
Data Mode: Edit
OpenQuery
QueryName: mySecondActionQuery
View: Datasheet
Data Mode: Edit
OpenQuery
QueryName: myThirdActionQuery
View: Datasheet
Data Mode: Edit
...
For action queries you do not have to close the query as OpenQuery runs process without physically opening anything to screen. You may want to incorporate the SetWarnings to False action in order to avoid the message prompt of how much records will be updated or inserted which is a default setting in Access. Because this is a potentially hazardous action, click Show All Actions to see this specific action.
Module
Public Sub RunQueries()
DoCmd.SetWarnings False
DoCmd.OpenQuery "myfirstSavedQuery"
DoCmd.OpenQuery "mySecondSavedQuery"
DoCmd.OpenQuery "myThirdSavedQuery"
...
DoCmd.SetWarnings True
End Sub
Similar to macros, you do not have to close action queries and can call the DoCmd.SetWarnings to suppress the message prompts with each update, insert, or make-table action.
The alternative to turning SetWarnings off and on is #LeeMac's solution which runs queries using the DAO's Database.Execute() command. In fact, because DAO is part of the MS Access object library, the above method can be run outside VBA as mentioned above.
Python
import os
import win32com.client
access_file = r'C:\Path\To\MyDatabase.accdb'
try:
oApp = win32com.client.Dispatch("Access.Application")
oApp.OpenCurrentDatabase(access_file)
db = oApp.Currentdb()
db.Execute("myFirstSavedQuery")
db.Execute("mySecondSavedQuery")
db.Execute("myThirdSavedQuery")
...
except Exception as e:
print(e)
finally:
oApp.Quit()
db = None; oApp = None
del db; del oApp
In its very simplest form, you can define a Sub such as:
Sub ExecuteQueries()
With CurrentDb
.Execute "MyQuery1"
.Execute "MyQuery2"
'...
.Execute "MyQueryN"
End With
End Sub
Or define this as a Function in a public module if you intend to invoke this from an MS Access Macro.
Here is another method if you don't have specific naming conventions utilizing an array for the query names in execution order:
Public Sub RunMasterUpdate()
Dim qryList As Variant
Dim i As Long
qryList = Array("QueryName1", "QueryName2", "QueryName3")
For i = LBound(qryList) To UBound(qryList)
CurrentDb.Execute qryList(i)
Next
End Sub
You can do something like this to run saved queries:
Dim db As DAO.Database, qry As DAO.QueryDef
Set db = CurrentDb
Debug.Print "-- Start --"
For Each qry In db.QueryDefs
If qry.Name Like pattern Then
Debug.Print qry.Name,
qry.Execute
Debug.Print "(" & qry.RecordsAffected & ")"
End If
Next
Debug.Print "-- End --"
db.Close()
Name the queries so that the alphabetical order matches the the expected execution order, e.g., 01_deleteCustomers, 02_appendCustomers, 03_etc.
Specify a pattern string or remove the If alltogether if you want to run all the queries.
Note that in the Visual Basic editor under menu Tools > References... I have selected Microsoft DAO 3.6 Object Library. (You might have another version)

How can I quickly start typing SQL in Microsoft Access?

In Microsoft Access 2016, I would like to click somewhere and start typing SQL of new query right away. But the shortest way to start typing SQL (which I found so far) is:
Select menu Create > Query Design.
In Show Table window, press Close button.
Switch to SQL view.
Start typing SQL.
Too cumbersome. Can be the above steps 1–3 reduced to a shorter procedure? Did I overlook some command?
The fastest I can do is to put commands for steps 1 and 3 to Quick Access Toolbar and then I can for example do Alt+5, Esc, Alt+6. But this still only executes steps 1–3.
The function below creates or alters a simple placeholder query, opens it in Design View, switches to SQL View with its text highlighted, and then deletes the text.
Use the function as the RunCode action of a macro and add the macro to the Quick Access Toolbar. Then you can click that icon and start typing your SQL.
Public Function NewQuery()
Const cstrQueryName As String = "USysQuery0"
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strSql As String
strSql = "SELECT Date() AS Today;"
Set db = CurrentDb
For Each qdf In db.QueryDefs
If qdf.Name = cstrQueryName Then
Exit For
End If
Next
If qdf Is Nothing Then
Set qdf = db.CreateQueryDef(cstrQueryName)
End If
qdf.SQL = strSql
qdf.Close
DoCmd.OpenQuery cstrQueryName, acViewDesign
DoCmd.RunCommand acCmdSQLView
DoCmd.RunCommand acCmdDelete
End Function
Option 1: deal with it.
By default, Access doesn't support anything faster than this.
Option 2: create a macro in your Access database to automate the task, and bind this to a key combination
You could write a macro to automate command bar actions, and bind that macro to a shortcut. I currently don't have time to look up the required commands but am certain it's possible.
Option 3: buy a plugin
There are plugins out there that immediately go to SQL view, support having the SQL and results on the same pane, have syntax highlighting, etc.
An example of such a plugin is Access SQL editor (I'm not affiliated in any way)
Option 4: Create your own.
Writing your own SQL editor that's better than what Access offers by default is very simple, because Access offers very little by default.
You can start off with a form with a textbox that takes the SQL, a save button, an execute button (requires a little VBA), and a subform that displays the query results. Then you already have something that allows you to open it and start typing right away.
While waiting for better answer, I created an AutoHotKey solution – a keyboard shortcut (restricted only to Access) to perform the above steps in English language version of Microsoft Access 2016:
SendMode Input ; // Choose mode of sending keystrokes
#IfWinActive, ahk_class OMain ahk_exe MSACCESS.EXE ; // Only in scope of MS Access app
+^n:: ; // Routine for Ctrl+Shift+N starts
Send !cqd ; // Run "Query Design" menu command
WinWaitActive,Show Table ahk_class #32770 ; // Wait for "Show Table" window
Send {Esc}!jqwq{End}{Left}{Space} ; // Send the rest of the keys
Return
#IfWinActive ; // end of #IfWinActive section
After pressing Ctrl+Shift+N and waiting a bit*, a new query window is open in SQL view and I can start typing SQL.
*) Sometimes, MS Access can be really slow on keyboard shortcuts accessing the ribbon and on switching from query Design View to SQL View.

Validation Error in Query yields no error, MS Access 2003 macro

I've inherited a script that runs and inserts records from one database into another database.
Everything appeared to be working fine until a few days ago when people complained that a record wasn't being pulled from one system to the other system.
I looked at the macro which was set to run with a scheduled task on Windows Server 2003. The macro sends it's logged output to SQL Server, and so I went there and looked for the appropriate log statements which read that one employee had been inserted into the database. However, when going to the other system, the employee that was reportedly transferred and inserted, was not there.
Upon further inspection pulled just the fields that were being selected from the one database, to the insert statement which added them to the other database.
When I tried to insert these values manually I received an SQL error that the fields were not allowed to be NULL or a zero length string. However, despite error checking in the script,
Function SomeFunc
On Error GoTo The_Err
DoCmd.SetWarnings False ' This statement seems a bit suspect to me...maybe fields being
' null or a zero length are just warnings? Seems wrong because
' I don't want a warning when my record will not be inserted,
' I want an error.
....
DoCmd.OpenQuery "qryAddNewEmp_S1toS2", acViewNormal, acAdd ' This is the statement that
' selects from the one db
' and inserts into the other db
...
The_Exit:
Exit Function
The_Err:
' MsgBox Error$ ' You can turn this on and off.
dmsg = "Error AUCP"
RunCheck dmsg, errArea, 0, 0
DoCmd.SetWarnings True
Resume The_Exit
End Function
Are the warnings turned off from DoCmd.SetWarnings False preventing the errors from being logged? Also should I just be doing the validation in the code instead, checking each of the values? It doesn't seem right to me, it seems like that's something that should be the responsibility of the database, since the database has that built in.
Instead of DoCmd.OpenQuery, use an approach like this ...
Dim db As DAO.Database
Set db = CurrentDb
db.Execute "qryAddNewEmp_S1toS2", dbFailOnError
Set db = Nothing
Then you will have no reason to turn SetWarnings off (False). And an INSERT failure will be captured by your error handler code, and the error will be logged (assuming RunCheck is a custom procedure which does the logging).

Intermittent error when attempting to control another database

I have the following code:
Dim obj As New Access.Application
obj.OpenCurrentDatabase (CurrentProject.Path & "\Working.mdb")
obj.Run "Routine"
obj.CloseCurrentDatabase
Set obj = Nothing
The problem I'm experimenting is a pop-up that tells me Access can't set the focus on the other database. As you can see from the code, I want to run a Subroutine in another mdb. Any other way to achieve this will be appreciated.
I'm working with MS Access 2003.
This is an intermittent error. As this is production code that will be run only once a month, it's extremely difficult to reproduce, and I can't give you the exact text and number at this time. It is the second month this happened.
I suspect this may occur when someone is working with this or the other database.
The dataflow is to update all 'projects' once a month in one database and then make this information available in the other database.
Maybe, it's because of the first line in the 'Routines' code:
If vbNo = MsgBox("Do you want to update?", vbYesNo, "Update") Then
Exit Function
End If
I'll make another subroutine without the MsgBox.
I've been able to reproduce this behaviour. It happens when the focus has to shift to the called database, but the user sets the focus ([ALT]+[TAB]) on the first database. The 'solution' was to educate the user.
This is an intermittent error. As this is production code that will be run only once a month, it's extremely difficult to reproduce, and I can't give you the exact text and number at this time. It is the second month this happened.
I suspect this may occur when someone is working with this or the other database.
The dataflow is to update all 'projects' once a month in one database and then make this information available in the other database.
Maybe, it's because of the first line in the 'Routines' code:
If vbNo = MsgBox("Do you want to update?", vbYesNo, "Update") Then
Exit Function
End If
I'll make another subroutine without the MsgBox.
I've tried this in our development database and it works. This doesn't mean anything as the other code also workes fine in development.
I guess this error message is linked to the state of one of your databases. You are using here Jet connections and Access objects, and you might not be able, for multiple reasons (multi-user environment, unability to delete LDB Lock file, etc), to properly close your active database and open another one. So, according to me, the solution is to forget the Jet engine and to use another connexion to update the data in the "other" database.
When you say "The dataflow is to update all 'projects' once a month in one database and then make this information available in the other database", I assume that the role of your "Routine" is to update some data, either via SQL instructions or equivalent recordset updates.
Why don't you try to make the corresponding updates by opening a connexion to your other database and (1) send the corresponding SQL instructions or (2) opening recordset and making requested updates?
One idea would be for example:
Dim cn as ADODB.connexion,
qr as string,
rs as ADODB.recordset
'qr can be "Update Table_Blablabla Set ... Where ...
'rs can be "SELECT * From Table_Blablabla INNER JOIN Table_Blobloblo
set cn = New ADODB.connexion
cn.open
You can here send any SQL instruction (with command object and execute method)
or open and update any recordset linked to your other database, then
cn.close
This can also be done via an ODBC connexion (and DAO.recordsets), so you can choose your favorite objects.
If you would like another means of running the function, try the following:
Dim obj As New Access.Application
obj.OpenCurrentDatabase (CurrentProject.Path & "\Working.mdb")
obj.DoCmd.RunMacro "MyMacro"
obj.CloseCurrentDatabase
Set obj = Nothing
Where 'MyMacro' has an action of 'RunCode' with the Function name you would prefer to execute in Working.mdb
I've been able to reproduce the error in 'development'.
"This action cannot be completed because the other application is busy. Choose 'Switch To' to activate ...."
I really can't see the rest of the message, as it is blinking very fast. I guess this error is due to 'switching' between the two databases. I hope that, by educating the user, this will stop.
Philippe, your answer is, of course, correct. I'd have chosen that path if I hadn't developed the 'routine' beforehand.
"I've been able to reproduce this behaviour. It happens when the focus has to shift to the called database, but the user sets the focus ([ALT]+[TAB]) on the first database. The 'solution' was to educate the user." As it is impossible to prevent the user to switch application in Windows, I'd like to close the subject.