Ignore SQL Query error in Excel Macro - sql

I have a set of queries in a EXCEL macro, that are executed when the macro is started
If, for some queries, the indicated fields don't exist in the database, executing the macro gives the following error:
Run-time error '3021': Either BOF or EOF is True, or the current record
has been deleted. requested operation requires a current record.
I want to protect the execution of the queries so that when this situation exists (lack of corresponding column in the database or any other kind of error related to the query), the macro simply continues the execution and don't output any kind of error, leaving the corresponding cell where the result supposedly would be outputted in blank.
Relevant piece of code:
Range("G7").Select
Set rs = conn.Execute("SELECT ....")
strResult = rs.Fields(0)

You need to check that BOF and EOF arent true in an if statement. Using your code above, you could do this like so:
Range("G7").Select
Set rs = conn.Execute("SELECT ....")
If Not rs.BOF and Not rs.EOF Then
strResult = rs.Fields(0)
End If

Related

VBA in MS Access: how to accelerate a loop operation on a recordset

In a database under my maintenance (MS Access 2010) I use a VBA procedure for cleaning up records, concretely: setting values in a field from "yes" to "no". The procedure loops through all records and sets the values in the respective field as required.
My database has about 900 records so far. Not too many, one should think.
My problem: the VBA procedure operates very slowly. On my current machine I have to wait for about 10 seconds until the loop has gone through the 900 records. That's impractical in everyday work.
What I need: I am looking for ways to speed this up, either through improvements to the code, or through a completely different approach.
Here is my procedure:
Private Sub WipeSelectionMarks_Click()
'Remove any filter that might be set to reduce the number of records in
'the form - We want here to operate on all records in the database!
Me.Filter = ""
Me.FilterOn = False
'Don't know if we really need two commands here; but at least it works
'Operate on a recordset that holds all the records displayed in the form
Dim rs As DAO.Recordset
Set rs = Me.RecordsetClone
rs.MoveFirst
Do Until rs.EOF
With rs
.Edit
!Int_IwSelectMove = False
.Update
End With
rs.MoveNext
Loop
'Message box with info what has been done
MsgBox "A total of " & rs.RecordCount & " records were updated by wiping the Move mark"
'Cleaning up
Set rs = Nothing
Me.Refresh
End Sub
Note 1: if a solution would be using an SQL command instead, I will be grateful for practical hints or examples. I use SQL commands at many places, still, getting put on the right track here would be helpful.
Note 2: Or can the VBA procedure be rewritten in a way that only records where the field in question has value "yes" are processed (these are usually only 20-30 of the 900), and those with "no" are left out?
You can use the UPDATE command:
CurrentDb.Execute "UPDATE YourTable SET Int_IwSelectMove = False"
can the VBA procedure be rewritten in a way that only records where
the field in question has value "yes" are processed
Indeed, and that may very well be the fastest method, as you will not have to requery the form:
With rs
Do Until .EOF
If !Int_IwSelectMove.Value = True Then
.Edit
!Int_IwSelectMove = False
.Update
End If
.MoveNext
Loop
.Close
End With
Or you could use FindFirst or a filtered recordset. Running SQL on the recordset of a form is usually the last option.

On Error During Loop

I have this code setup which inserts data into a table from another, and it is setup on a loop. I would like for it to check to see if the record it was supposed to upload actually uploaded and if it didn't I would like for it to try again, then after the second try if it still cant I would like for it to terminated the code. Basically:
Before this code runs a connection check is run prior then if there is a connection the record uploads, then during the loop process I would like for it to run a select statement to find the record that was just uploaded on the new table. If it finds it, then it continues onto the next record, if it doesn't it tries one more time, then stops if it still cant. This is one way for me to verify the connection.
It is a way to kind of test the connection during the actual upload process so I don't have to build in a connection check after each record, thus saving time. I am just unsure how to build in an On Error in a loop, are there any ideas? The bottom of the code looks like:
qdf.ReturnsRecords = False
On Error GoTo Update_qdfError
qdf.Execute dbFailOnError
On Error GoTo 0
rs.MoveNext
Loop
rs.Close
Set qdf = Nothing
Set cdb = Nothing
Set rs = Nothing
Exit Function
Update_qdfError:
For Each err In DAO.Errors
MsgBox err.Description, vbCritical, "Error " & err.Number
Next
End Function
Since VBA doesn't have structured error handling, you would do the error testing one line at a time in your loop by setting the On Error property and then checking the ERR object. First, set it up so it falls over to the next line of code like this.
On Error Resume Next
Then, test.
Do While rs.EOF
rs.Fields("fieldName").value =value
if Err.Number >0 then
'you had an error
end if
Loop

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).

Preserving Relationships on imported data

I'll start with the background story before explaining the problem with my code. I'm using MS Access 2010. I've been able to import a table of data with two columns. Then I was able to curate the data by adding fields with appropriate values to the imported table. Now, I need to take the curated table and integrate it into my data base. However, I cannot use any of Microsofts built in queries as none of these appear to be able to do what I need. The integration breaks the table apart, yes, but it needs to preserve the relationships of the data in each record.
To this end I've been writing some code in VBA:
Function IntegrateNIRData(curatedTable, queryRecords)
On Error GoTo Error_Handler
Dim db As DAO.Database
Dim rsCuratedTable, rsDBRecords As DAO.Recordset
Dim iCount As Integer
Set db = CurrentDb()
Set rsCuratedTable = db.OpenRecordset(curatedTable, dbOpenTable) 'open the recordset for use (table, Query, SQL Statement)
Set rsDBRecords = db.OpenRecordset("NIR_Samples_verify", dbOpenDynaset, dbExecDirect, dbOptimisticValue)
With rsCuratedTable
If Not (.BOF And .EOF) Then
Do While Not .EOF
' Rest of your code here.
rsDBRecords.AddNew
'Assign Fields here.
rsDBRecords![Product Name] = rsCuratedTable![productName]
rsDBRecords![Lot Number] = rsCuratedTable![lotNumber]
rsDBRecords!counts = rsCuratedTable![counts]
rsDBRecords![subsets] = rsCuratedTable![subsets]
rsDBRecords![Date Taken] = rsCuratedTable![dateTaken]
rsDBRecords.Update
rsDBRecords.Bookmark = rsDBRecords.LastModified
.MoveNext
Loop
End If
End With
rsCuratedTable.Close 'Close the recordset
rsDBRecords.Close 'Close the recordset
Error_Handler_Exit:
On Error Resume Next
'Cleanup after ourselves
Set rs = Nothing
Set db = Nothing
Exit Function
Error_Handler:
MsgBox "MS Access has generated the following error" & vbCrLf & vbCrLf & "Error Number: " & _
Err.Number & vbCrLf & "Error Source: IntegrateNIRData" & vbCrLf & "Error Description: " & _
Err.Description, vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
The Function hangs on this line, the second OpenRecordset:
Set rsDBRecords = db.OpenRecordset("NIR_Samples_verify", dbOpenDynaset, dbExecDirect, dbOptimisticValue)
To my understanding this may have something to do with Workspaces and the Jet engine not accepting a ms query that spans multiple tables. Of course, I could also be way off. Any advice at this point would be greatly appriciated.
Update:
Several of you have asked similar questions so I felt I should clarify the following:
1) NIR_Samples_verify is an MS access select query that generates a table of records from several of the tables in the database.
2) I keep getting two errors depending on what I set the RecordsetOptionEnum and LockTypeEnum to in the OpenRecordset method.
One is Error Number 3027 Database is Read-Only
Two is Error Number 3001 Invalid Arguement
3) To my understanding the rest of the code should be fine, it is just the OpenRecordset method that is causing the problem.
Update 2:
I am thinking that maybe access is not capable of doing what I would like. Let me illustrate. If I had two tables both with primary keys and these keys are referenced in a third table that links the two tables causing a many-to-many relationship, then the code would have to not only add the new data to the two tables, but also generate an appropriate record in the third table to maintain the relationship in the data. Hope that makes since. I do appriciate the help and experience.
Update 3:
Have been searching the net and found the following:
From this post it says the query is only updatable when:
1) It is based on a single table.
2) It is based on a query based on a single table.
3) It is based on a query based on tables with a one-to-one relationship.
Not knowing what the contents of NIR_Samples_verify are, I'd be highly suspicious of the dbExecDirect
From the help file
dbExecDirect
"Runs a query by skipping SQLPrepare and directly calling
SQLExecDirect (ODBCDirect workspaces only). Use this option only when
you’re not opening a Recordset based on a parameter query. For more
information, see the "Microsoft ODBC 3.0 Programmer’s Reference." "
I don't see you supplying any parameters.
-- Edit --
Typically I'll open a record set like this
Set rsDBRecords = db.OpenRecordset("select bar from foo where bar > 10", _
dbOpenDynaset, _
dbSeeChanges)
(Especially if I want to alter the data init)
Hopefully that'll move you further in your project.
-- Edit 2 --
It sounds like NIR_Samples_verify is to complicated to be edited. Actually, given that it is a join of multiple tables doing an Add on it doesn't make much sense, and Update MIGHT make sense in some cases.
Your ultimate solution is really going to be doing multiple Adds on multiple record sets (one for each table being referenced in NIR_Samples_verify); much like if you were entering the data into the DB by hand. You add the records that aren't dependant on anything else first (remembering to grab keys to use in the dependant tables).
As it turns out my hunch was correct. The problem had to do with MS Access having updatable and non-updatble queries (See my edits of the question). The main problem was not only does Microsoft not make this information apparent, but there is no master list on their site either. Thank you everyone for the help. Feel free to see this article for more details.

MS Access - execute a saved query by name in VBA

How do I execute a saved query in MS Access 2007 in VBA?
I do not want to copy and paste the SQL into VBA. I rather just execute the name of the query.
This doesn't work ... VBA can't find the query.
CurrentDb.Execute queryname
You can do it the following way:
DoCmd.OpenQuery "yourQueryName", acViewNormal, acEdit
OR
CurrentDb.OpenRecordset("yourQueryName")
You should investigate why VBA can't find queryname.
I have a saved query named qryAddLoginfoRow. It inserts a row with the current time into my loginfo table. That query runs successfully when called by name by CurrentDb.Execute.
CurrentDb.Execute "qryAddLoginfoRow"
My guess is that either queryname is a variable holding the name of a query which doesn't exist in the current database's QueryDefs collection, or queryname is the literal name of an existing query but you didn't enclose it in quotes.
Edit:
You need to find a way to accept that queryname does not exist in the current db's QueryDefs collection. Add these 2 lines to your VBA code just before the CurrentDb.Execute line.
Debug.Print "queryname = '" & queryname & "'"
Debug.Print CurrentDb.QueryDefs(queryname).Name
The second of those 2 lines will trigger run-time error 3265, "Item not found in this collection." Then go to the Immediate window to verify the name of the query you're asking CurrentDb to Execute.
To use CurrentDb.Execute, your query must be an action query, AND in quotes.
CurrentDb.Execute "queryname"
Thre are 2 ways to run Action Query in MS Access VBA:
You can use DoCmd.OpenQuery statement. This allows you to control these warnings:
BUT! Keep in mind that DoCmd.SetWarnings will remain set even after the function completes. This means that you need to make sure that you leave it in a condition that suits your needs
Function RunActionQuery(QueryName As String)
On Error GoTo Hell 'Set Error Hanlder
DoCmd.SetWarnings True 'Turn On Warnings
DoCmd.OpenQuery QueryName 'Execute Action Query
DoCmd.SetWarnings False 'Turn On Warnings
Exit Function
Hell:
If Err.Number = 2501 Then 'If Query Was Canceled
MsgBox Err.Description, vbInformation
Else 'Everything else
MsgBox Err.Description, vbCritical
End If
End Function
You can use CurrentDb.Execute method. This alows you to keep Action Query failures
under control. The SetWarnings flag does not affect it. Query is executed always without warnings.
Function RunActionQuery()
'To Catch the Query Error use dbFailOnError option
On Error GoTo Hell
CurrentDb.Execute "Query1", dbFailOnError
Exit Function
Hell:
Debug.Print Err.Description
End Function
It is worth noting that the dbFailOnError option responds only to data processing failures. If the Query contains an error (such as a typo), then a runtime error is generated, even if this option is not specified
In addition, you can use DoCmd.Hourglass True and DoCmd.Hourglass False to control the mouse pointer if your Query takes longer