Access VBA DoCmd.OpenQuery Issue - ms-access

I have a really annoying issue that I'm having which has worked perfectly well historically in other Access dbs.
I have, as a test, a form with a button.
The click event button is to run a simple query, this is the VBA:
Private Sub Command0_Click()
DoCmd.SetWarnings False
DoCmd.OpenQuery "mktbl_qry_Useable_Leads", acViewNormal, acEdit
DoCmd.SetWarnings True
End Sub
Very simple and straight forward, however, I constantly get the Macros pop up list asking me to select a sub routine.
Is there a reason for this?
I have tried other queries with the same result and also another blank form with the same issue. Its driving me insane.
Help is much appreciated.

You can use the Database object to run query's. For a simple query you don't need to create a query in the database window
CurrentDB.Execute "qdRunUpdateQuery", dbFailOnError + dbSeeChanges
or
CurrentDB.Execute "Update article set price = 10 where id=23", dbFailOnError + dbSeeChanges

Related

How to force an Access query datasheet to refresh its data

I am new on access and what I am trying to do is a select with a criteria so I created a query with the wizard and seted the criteria with a text from a form ([Forms]![Form1]![Transacao]) and created a button to run the query at the first time works great but when I type something else and click the button the datas do not refresh. What I have to do to refresh? I've tryed to add refresh on the event click of the button and did not work.
Thanks in advance for your help.
In Access, a query is usually opened in a default Datasheet view. This default datasheet is contained in a window (or tab) that is only accessible using Macros or DoCmd in VBA.
Once a query window is open, its data will not necessarily update automatically when new records are added to the underlying table(s). The datasheet needs to be "requeried". (Incidentally, the term "refresh" is usually reserved to mean "redrawing" a window on the screen and has nothing to do with the data. This is especially the case in programming and development environments which deal with data and drawing/painting windows and controls on the screen.)
Here is one way to force a query to update its data (when open in its default datasheet view):
DoCmd.OpenQuery "QueryName"
DoCmd.Requery
Calling OpenQuery should also activate the query window if it is already open. If you find that the windows does not activate, you can also call DoCmd.SelectObject acQuery, "QueryName" before DoCmd.Requery.
The DoCmd methods correspond to Macro actions, so if the query is activated by a Macro, just add the Requery action to the macro after the OpenQuery or SelectObject actions. Leave the Control Name parameter of the Requery action blank to force the entire query to updated.
I know this question is a bit stale at this point, but since I couldn't find a suitable answer to this question and the above answer didn't work for me (and still hasn't been accepted), I thought I'd offer my solution for those few poor saps still stuck developing applications in Access. My use case was slightly different (changing the underlying SQL of a query, then opening/refreshing it), but the same principle could be applied. The gist is to first check to see if the query is open and close it if it is. Then open it up again.
To do this, paste this code into a VBA module:
Public Function open_or_refresh_query(query_name As String, Optional sql_str As String = "")
' Refresh or open an existing query
' query_name: Name of the query
' sql_str: optional new SQL string if changing the underlying SQL. If not given,
' the query will open with its existing SQL string
On Error GoTo err_handler
Dim qdf As QueryDef
' Loop through each query in the DB and find the one of interest by name
For Each qdf In CurrentDb.QueryDefs
If qdf.Name = query_name Then
' If the query is open, close it
If SysCmd(acSysCmdGetObjectState, acQuery, query_name) <> 0 Then
DoCmd.Close acQuery, query_name, acSaveNo
End If
Exit For
End If
Next qdf
Set qdf = CurrentDb.QueryDefs(query_name)
' Reset the SQL if new SQL string was given
If Len(sql_str) > 0 Then qdf.sql = sql_str
' Close the QueryDef object to release resources
qdf.Close
' Open the query in default datasheet view
DoCmd.OpenQuery query_name
exit_function:
Exit Function
err_handler:
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "Error"
Resume exit_function
End Function
At this point you could call this function from any other VBA code in your project. To open/refresh a query from a macro as the OP wanted to do, create a new macro and add a RunCode action with open_or_refresh_query("my_query") in the Function Name field, changing my_query to the name of your query.

How to update a subform after performing a DELETE query?

I have a form, myForm, that includes a subform, mySubform. The records in mySubform have a many to one relationship with the record source of the myForm, as expected, and There is a combobox in mySubform, myCombo, whose values is linked to one of the columns of the record source of mySubform.
I have been having difficulty to delete a record in mySubform, by erasing the current value in myCombo. I have put the code below under the OnChange event of myCombo (after trying many different alternatives).
Please not that I have simplified the delete query and the actual one works fine in the back-end (SQL Server). However, after performing the delete operation, I get this error when the execution reaches Me.Requery:
Error 3162 You tried to assign a Null value to a variable that is not a Variant data type.
Also, after having the degugger skip the Me.Requery and going back to the form, the deleted record shows "#DELETED" in every combobox and textbox for that particular record, and mySubform is not any good beyond that point.
I have looked up very many resources and tried different statements instead of Me.Requery, but none has worked so far. Help is much appreciated.
Private Sub myCombo_Change()
If IsNull(Me.myCombo.Text) Or Me.myCombo.Text = vbNullString Then
Dim strSql As String
strSql = "DELETE FROM mySubformRecordSource WHERE PrimaryKeyColumn= " & theCurrentPrimaryKeyValueForTheValueIn_MyCombo
CurrentDb.Execute strSql
Me.Requery
End If
End Sub
I solved the problem by putting Me.Dirty = False after reading this discussion:
Editing Record issues in Access / SQL (Write Conflict)
Private Sub myCombo_Change()
If IsNull(Me.myCombo.Text) Or Me.myCombo.Text = vbNullString Then
Me.Dirty = False
Dim strSql As String
strSql = "DELETE FROM mySubformRecordSource WHERE PrimaryKeyColumn= " & theCurrentPrimaryKeyValueForTheValueIn_MyCombo
CurrentDb.Execute strSql
Me.Requery
End If
End Sub
The form and subform work flawlessly now, but I still dont quite understand the complete logic behind the solution. It has something to do with Me.Dirty=False saving the changes to the content of a non-nullable (or nullable, both work fine) column before programmatically delete the entire record. How exactly though, I dont know and will appreciate input.
Not entering on the choices of used cursors etc I think that the solution could be simply "requery" the parent form called by the code of the subform.
But i also have found similar situation in past projects that i made (sorry not beeing exaustive, i'm not using VBA anymore) and the requery command was done on subform but from the main form code, something like this:
If local_procedures.deleteContratto(anagrafica_id_par, servizio_id_par) = OK Then
Me.subfrm_contratti_frm_anagrafica.Requery
...
In this case you could delegate parent form to call delete procedure and update logic (.Requery) of the child form, by mean of an event raised from subForm listen on parent for example.
For the sake of completeness i started hinting about cursors because i think it's the underlying reason for these kind of sync failures between recordsets, the parent form one and the child form one.
There are many advices about using dbSeeChanges with db.Execute on SQL Server backend and the resulting default cursors.
UPDATE
Here's a code snippet where a custom event is raised from child to be caught from parent that is delegated to execute code regarding the child.
This pattern doesn't break the parent-child/one-to-many logic, assuming parent has more decision power than child.
' in mySubform
' assuming theCurrentPrimaryKeyValueForTheValueIn_MyCombo is a Long ID
Public Event DeleteRequest(record_ID As Long)
Private Sub myCombo_Change()
If IsNull(Me.myCombo.Text) Or Me.myCombo.Text = vbNullString Then
RaiseEvent DeleteRequest(theCurrentPrimaryKeyValueForTheValueIn_MyCombo)
End If
End Sub
' in myForm
Private WithEvents frmMySubform As Form__mySubform
' you could have more instances of mySubform concerning different issues or subrecords and manage each one correctly ...
Private WithEvents frmMySubform2nd As Form__mySubform
Private Sub frmMySubform_DeleteRequest(record_ID As Long)
Dim strSql As String
strSql = "DELETE FROM mySubformRecordSource WHERE PrimaryKeyColumn= " & record_ID
CurrentDb.Execute strSql
With Me.frmMySubform.Form
.Requery
' ...
end With
End Sub
Private Sub frmMySubform2nd_DeleteRequest(record_ID As Long)
Dim strSql As String
strSql = "DELETE FROM my2ndSubformRecordSource WHERE PrimaryKeyColumn= " & record_ID
CurrentDb.Execute strSql
With Me.frmMySubform2nd.Form
.Requery
' ...
end With
End Sub

access 2010 bloat with forms based on pass through queries

My access 2010 database has super massive bloat. It has gone from 44mb to 282mb in just a few runs of the VBA. Here is what I have set up:
Local tables - these calculate statistics on forms and generally don't move around too much.
Pass through queries - my personal suspect. While viewing a form, if the user clicks on a record I run a pass through query using the record the user clicked on. So user clicked on "joe", pass through query runs with sql string = "select * from sqlserver where name= " &[forms]![myform]![firstname]
After this query runs, my vba DELETES the pass through query, then recreates it after another record is selected. so the user goes back to the list of names, and clicks BRIAN. then my vba deletes the pass through query and creates another one to select all records named brian from sql server, using the same code as above.
Forms - my forms are using the pass through queries as sources.
Is what I'm doing not very smart? How can I build this better to prevent access from exploding in file size? I tried compact and repair, as well as analyze DB performance in access 2010. Any help at all is appreciated, I've been googling access2010 bloat and have read about temptables as well as closing DAO (which I am using, and which I did explicitly close). Thanks!
Here is some code for 1 of the forms i'm using -
Private Sub name_Click()
'set dims
Dim db As DAO.Database
Dim qdExtData As QueryDef
Dim strSQL As String
Dim qdf As DAO.QueryDef
'close form so it will refresh
DoCmd.Close acForm, "myform", acSaveNo
'delete old query so a new one can be created with the same name
For Each qdf In CurrentDb.QueryDefs
If qdf.Name = "QRY_PASS_THROUGH" Then
DoCmd.Close acQuery, "QRY_PASS_THROUGH", acSaveNo
DoCmd.SetWarnings False
DoCmd.DeleteObject acQuery, "QRY_PASS_THROUGH"
DoCmd.SetWarnings True
Exit For
End If
Next
Set db = CurrentDb
'sql for the data
strSQL = "select fields from (table1 inner join table2 on stuff=stuff and stuff=stuff) left join table3 on stuff=stuff and stuff=stuff where flag='P' and table.firstname = " & [Forms]![myform]![firstname]
Set qdExtData = db.CreateQueryDef("QRY_PASS_THROUGH")
'how you connect to odbc
qdExtData.Connect = "ODBC;DSN=server;UID=username;PWD=hunter2;"
qdExtData.SQL = strSQL
DoCmd.OpenForm ("names")
Forms!returns!Auto_Header0.Caption = "Names for " & Me.name & " in year " & Me.year
qdExtData.Close
db.Close
qdf.Close
Set db = Nothing
Set qdf = Nothing
End Sub
There no reason I can think of to not bind the form to a view and use the “where clause” of the open form command. It would eliminate all that code.
You could then simply use:
strWhere = "table.FirstName = '" & me.FirstName & "'"
Docmd.OpenForm "Names”,,,strWhere
Also, it makes little or no sense that a C + R does not return free space. Something else here is seriously wrong.
Also, you really don’t need to delete the query each time. Just assume that the pass-through ALWAYS exists and then use this:
strSQl = “your sql goes here as you have now”
Currentdb.Querydef("MyPass").SQL = strSQL
Docmd.Openform “your form”
The above is all you need. I count about 3 lines of code here that will replace all what you have now. Note that of course the connection string info is saved with the pass-though and does not need to be set or messed with each time.
I would also do a de-compile of your database. I have a short cut setup on all my dev machines so I can just right click to de-compile. Here is some info on de-compile:
http://www.granite.ab.ca/access/decompile.htm
So really, I don’t know why you not using the where clause of the open form? Just bind the form to a SQL view without any conditions. Then use the open form command – you only pull records down the network pipe that match your criteria.
And note how you can stuff your SQL directly into the .SQL property of the query def as above shows – again no need for that delete code and this also means you don’t mess with connection info in code either. Again about 3 lines in total.

Creating and writing data to a table is MS Access

EDIT: Changed as I have a different issue with the same code
2nd Edit: Adding additional code that seems to be casuing the issue
I have created a vba program in access that aggregates data from a number of external sources and write the results into a new table. Ideally when I run the program I want to wipe out all of the data that is currently in the table and replace it with my new data. I am currently currently deleting all of the data in the table... then writing my new data
Here is the code for reference
Function getTestFixtures(FixtureName As String) As Recordset
Dim db As Database
Set db = OpenDatabase(GetDBPath & "TestFixtures.xlsx", False, False, "Excel 12.0;HDR=Yes;")
If db Is Nothing Then
MsgBox "Can't find the file!", vbExclamation, ThisWorkbook.Name
Exit Function
End If
Set getTestFixtures = db.OpenRecordset("Select * from [" & FixtureName & "$]")
End Function
The recordset created above is modified and the output data is placed in a dictionary and passed to this function.
Sub Write_OTDC_Data(POlist As Dictionary)
Dim Rst As Recordset
DoCmd.SetWarnings False
DoCmd.runsql "Delete * from [OTDC Results]"
DoCmd.SetWarnings True
Set Rst = CurrentDb.OpenRecordset("OTDC Results")
With Rst
For Each key In POlist.Keys
.AddNew
For i = 0 To 9
.Fields(i).value = POlist(key)(i)
Next
.Update
Next
.Close
End With
End Sub
My Problem is that I get the following error if I try to change anything after running both of the above procuedures.
Running either in isolation does not generate the error.
I'm unsure whether this question is still unresolved. In case it's not, I have some suggestions for you to try, but not a lot of confidence they will cure the problem.
Try DoCmd.TransferSpreadsheet to import the sheet's data into a scratch table instead of using OpenDatabase with the workbook.
In your MsgBox, I wonder whether ThisWorkbook.Name means anything to an Access application. Aside from that, I would check whether the workbook exists, then open it (or import the sheet from it) only if the file is found.
If Len(Dir(GetDBPath & "TestFixtures.xlsx")) = 0 Then
'not found
Else
'use it
End If
Actually I'm unclear why you don't get an error from OpenDatabase if the workbook file doesn't exist. And that makes me suspicious of DoCmd.SetWarnings False Never, ever turn SetWarnings off. Doing so suppresses important information. And it is completely unnecessary. Set a DAO.Database object variable to CurrentDB(), then use this instead:
dbObjectVariable.Execute "Delete from [OTDC Results]", dbFailOnError
Add an error handler to deal with any problems dbFailOnError exposes.
Finally, this bears repeating because it's so important. NEVER turn SetWarnings off.

Find not behaving consistently in .ADP form

Is there something wrong with the following code (in an ADP form) ?
Private Sub cmbSearchCode_AfterUpdate()
Me.Recordset.Find "usr_cde = '" & ctl & "'"`
ctl = null
end sub
It behaves erratically: the first times after opening the form, it works, then suddenly it does not seem to do anything anymore.
I replaced it by this code that seems to have no problems:
With Me.RecordsetClone
.Find "usr_cde = '" & ctl & "'"
Me.Bookmark = .Bookmark
End With
ctl = Null
Any explanation ?
The client is A2003, the server is SS 2000 (I know it's old, nothing I can do about it !)
The Form.Recordset Property is a fairly new addition to access, there are a couple of bits in the helptexts, it's how they combine that I believe is causing your issue.
If a form is based on a query, for example, referring to the
Recordset property is the equivalent of cloning a Recordset object by
using the same query. However, unlike using the RecordsetClone
property, changing which record is current in the recordset returned
by the form's Recordset property also sets the current record of the
form.
So while it doesn't look like it at first, it does actually clone the recordset and create a new copy of it. Just it keeps it synchronised.
So you have a new Recordset Object and here's the rub:
A new Recordset object is automatically added to the Recordsets
collection when you open the object, and is automatically removed when
you close it.
First time round, you clone the recordset find on usr_cde, and the form magically sets the current record to match.
Second time round, you clone the recordset find on usr_cde, but the magical record synchronisation is still stuck on the first copy of the recordset that has been persisted in the Recordsets collection.
So you just need to close the recordset, but to make sure you don't just create another copy and close that do the following:
'untested
Private Sub cmbSearchCode_AfterUpdate()
Dim rs as adodb.Recordset
Set rs = Me.Recordset
rs.Find "usr_cde = '" & ctl & "'"`
rs.Close
ctl = null
end sub
I haven't had a chance to test this, but you already have a working solution using .Bookmark. I hope this explains the unexpected behaviour.