What is the proper way to add retry loop to VBA code - vba

In the following code, we occasionally get collision errors. If it would wait a second and retry, it would go through.
I want it to try once. If it fails, log the error and retry. If it fails 3 times, MsgBox to the user, give up and return. The only way I can think of is using a GOTO back to the ExeHandler. Seems there should be a better way.
Public Function RunADO(strContext As String, strSQL As String, Optional intErrSilent As Integer = 0, Optional intErrLog = -1) As Integer
On Error GoTo ErrHandler
ExeHandler:
PostToLog strContext, "SQL: " & strSQL
CurrentProject.Connection.Execute strSQL, dbFailOnError
RunADO = -1
Exit Function
ErrHandler:
RunADO = 0
If intErrSilent = 0 Then
MsgBox Err.Number & ": " & Err.Description, vbCritical, "Run ADO"
End If
If intErrLog = -1 Then
PostErrorToLog Err.Number, strContext, Err.Source & ":" & Err.Description & ": " & "SQL: " & strSQL
End If
End Function

Perhaps something like this...
Option Explicit
Private Const MaxRetries As Long = 3
Public Function RunADO(strContext As String, strSQL As String, Optional intErrSilent As Integer = 0, Optional intErrLog = -1) As Integer
Dim attemptCounter As Long
For attemptCounter = 1 To MaxRetries
RunADO = RunADOInternal(strContext, strSQL, intErrSilent, intErrLog)
If RunADO = -1 Then
Exit Function
End If
If intErrSilent = 0 And attemptCounter >= MaxRetries Then
MsgBox Err.Number & ": " & Err.Description, vbCritical, "Run ADO"
RunADO = 0
Exit Function
End If
If intErrLog = -1 Then
PostErrorToLog Err.Number, strContext, Err.Source & ":" & Err.Description & ": " & "SQL: " & strSQL
End If
Application.Wait (Now + TimeValue("0:00:01"))
Next attemptCounter
End Function
Private Function RunADOInternal(strContext As String, strSQL As String, Optional intErrSilent As Integer = 0, Optional intErrLog = -1) As Integer
On Error GoTo ErrHandler
PostToLog strContext, "SQL: " & strSQL
CurrentProject.Connection.Execute strSQL, dbFailOnError
RunADO = -1
Exit Function
ErrHandler:
RunADO = 0
End Function

Consider using a Do / Loop statement that ends when the maximum number of attempts have been made.
Public Function RunADO(strContext As String, _
strSQL As String, _
Optional intErrSilent As Integer, _
Optional intErrLog = -1) As Integer
Dim Attempts As Integer
Do While Attempts < 3
On Error Resume Next
PostToLog strContext, "SQL: " & strSQL
CurrentProject.Connection.Execute strSQL, dbFailOnError
If Err.Number Then
If intErrSilent = 0 Then
MsgBox Err.Number & ": " & Err.Description, vbCritical, "Run ADO"
End If
If intErrLog = -1 Then
PostErrorToLog Err.Number, strContext, _
Err.Source & ":" & Err.Description & ": " & "SQL: " & strSQL
End If
Attempts = Attempts + 1
Else
RunADO = -1
Exit Do
End If
Loop
End Function

Related

Loop through continuous form records and adding them to a table

Good afternoon,
I have a bound continuous form with a yes/no checkbox for each of the records shown. I'd like to have my users be able to click a "Select all" control and automatically have each one of those records be added to a different table.
So far, I have my loop working but not quite sure where to insert the proper code to add the records. I am using a frame control with a with a "Select all" "Deselect all" options. The [PrintPart] is the Yes/No field in the form.
Many thanks.
Private Sub SelectionFrame_Click()
Dim SQL as String
On Error GoTo errHandler
SQL = "INSERT INTO PARTS_T ([PRINTORDER], [PART_TITLE], [PARTID])
VALUES (" & Me.RecordsetClone.RecordCount + 1 & "," & Chr(34) &
Me.PART_TITLE & Chr(34) & "," & Me.PARTID & ");"
'Toggle select/deselect all
Select Case Me.SelectionFrame
Case 1 'Select
With Me.RecordsetClone
.MoveFirst
Do While Not .EOF
.Edit
!PrintPart = True
RunSQLCode
.Update
.MoveNext
Loop
End With
Case 2 'Deselect
With Me.RecordsetClone
.MoveFirst
Do While Not .EOF
.Edit
!PrintPart = False
.Update
.MoveNext
Loop
End With
End Select
Exit Sub
errHandler:
MsgBox "The following error has occurred: " & vbNewLine & vbNewLine & "Error Number: " & Err.Number & vbNewLine & "Error Description: " & Err.Description, vbCritical, "Error - " & APPNAME
End Sub
Don't run repeated SQL. DAO is way simpler and faster:
Private Sub SelectionFrame_Click()
Dim Source As DAO.Recordset
Dim Target As DAO.Recordset
Dim SQL As String
Dim PrintPart As Boolean
Dim PrintOrder As Long
On Error GoTo errHandler
Set Source = Me.RecordsetClone
PrintOrder = Source.RecordCount
If PrintOrder > 0 Then
SQL = "Select * From PARTS_T"
Set Target = CurrentDb.OpenRecordset(SQL, dbOpenDynaset, dbAppendOnly)
PrintPart = Me.SelectionFrame
Source.MoveFirst
While Not Source.EOF
If Source!PrintPart.Value <> PrintPart Then
Source.Edit
Source!PrintPart.Value = PrintPart
Source.Update
End If
Target.AddNew
Target!PRINTORDER.Value = PrintOrder + 1
Target!PART_TITLE.Value = Source!PART_TITLE.Value
Target!PARTID.Value = Source!PARTID.Value
Target.Update
Wend
Target.Close
End If
Source.Close
Exit Sub
errHandler:
MsgBox "The following error has occurred: " & vbNewLine & vbNewLine & "Error Number: " & Err.Number & vbNewLine & "Error Description: " & Err.Description, vbCritical, "Error - " & APPNAME
End Sub

MS Access VBA export table to text file

I am using the code below to export data from Access table to text file so i can use it in mySQL.
My export code:
Sub ExpTblCity()
On Error GoTo Err_Handler
Dim t, sText, rText, LResult As String
Close #1
t = "INSERT INTO `tblcity` (`city_id`,`city_name`,`city_enabled`) VALUES "
Dim rst As DAO.Recordset
Open Application.CurrentProject.Path & "\2-TblCity.txt" For Output As #1
Print #1, t
Set rst = CurrentDb.OpenRecordset("SELECT * FROM tblCity", dbOpenSnapshot)
Do While Not rst.EOF
rText = "'NULL'"
sText = "('" & rst!CityID & "','" & rst!City & "','0'),"
LResult = Replace(sText, rText, "NULL")
Print #1, LResult
rst.MoveNext
Loop
rst.Close
Set rst = Nothing
t = ""
sText = ""
rText = ""
LResult = ""
Close #1
Exit_This_Sub:
Exit Sub
Err_Handler:
If Err = 0 Then
ElseIf Err = 94 Then
Resume Next
ElseIf Err = 3265 Then
Resume Next
Else
MsgBox "Error #: " & Err.Number & " " & Err.Description
End If
Resume Exit_This_Sub
End Sub
Output from the above code:
INSERT INTO `tblcity` (`city_id`,`city_name`,`city_enabled`) VALUES
('1','London','0'),
('2','Paris','0'),
('3','Rome','0'),
('4','Athens','0'),
('5','Madrit','0'),
The code is working fine BUT i am trying to replace IN THE LAST ROW the last , with ;.
Correct output:
...
('4','Athens','0'),
('5','Madrit','0');
Any idea.
It will be convenient to save it by using an array and use the Join Function.
Sub ExpTblCity()
On Error GoTo Err_Handler
Dim t As String, sText As String, rText As String, LResult As String
Dim vResult() As Variant
Dim n As Long
Close #1
t = "INSERT INTO `tblcity` (`city_id`,`city_name`,`city_enabled`) VALUES "
Dim rst As DAO.Recordset
Open Application.CurrentProject.Path & "\2-TblCity.txt" For Output As #1
Print #1, t
Set rst = CurrentDb.OpenRecordset("SELECT * FROM tblCity", dbOpenSnapshot)
Do While Not rst.EOF
n = n + 1
rText = "'NULL'"
sText = "('" & rst!CityID & "','" & rst!City & "','0')"
ReDim Preserve vResult(1 To n)
sText = Replace(sText, rText, "NULL")
vResult(n) = sText
'Print #1, LResult
rst.MoveNext
Loop
sText = Join(vResult, "," & vbCrLf) & ";"
Print #1, sText
rst.Close
Set rst = Nothing
t = ""
sText = ""
rText = ""
LResult = ""
Close #1
Exit_This_Sub:
Exit Sub
Err_Handler:
If Err = 0 Then
ElseIf Err = 94 Then
Resume Next
ElseIf Err = 3265 Then
Resume Next
Else
MsgBox "Error #: " & Err.Number & " " & Err.Description
End If
Resume Exit_This_Sub
End Sub
Please, try changing of this line:
sText = "('" & rst!CityID & "','" & rst!City & "','0'),"
with:
If rst.EOF then
sText = "('" & rst!CityID & "','" & rst!City & "','0');"
Else
sText = "('" & rst!CityID & "','" & rst!City & "','0'),"
End If

How can I manage error code SQL in MS access form database?

I want manage SQL server error code in access form
sample duplicate error from SQL server
In Access VBA, you need to use:
On Error GoTo Error_Handler
' YOUR CODE HERE
.
.
.
Return_Label:
Exit Function
Error_Handler:
'What goes here depends on the data access model
Resume Return_Label
You may have to retrieve the Errors collection of the Error object as described here.
It shows this example code:
Sub DescriptionX()
Dim dbsTest As Database
On Error GoTo ErrorHandler
' Intentionally trigger an error.
Set dbsTest = OpenDatabase("NoDatabase")
Exit Sub
ErrorHandler:
Dim strError As String
Dim errLoop As Error
' Enumerate Errors collection and display properties of
' each Error object.
For Each errLoop In Errors
With errLoop
strError = _
"Error #" & .Number & vbCr
strError = strError & _
" " & .Description & vbCr
strError = strError & _
" (Source: " & .Source & ")" & vbCr
strError = strError & _
"Press F1 to see topic " & .HelpContext & vbCr
strError = strError & _
" in the file " & .HelpFile & "."
End With
MsgBox strError
Next
Resume Next
End Sub

VB6 Outlook automation

I have a block of code that will send emails using Outlook on the users PC. However when we email a bigger amount, it seems as though outlook isn't working as fast as the application so the application opens outlook sends the first email, but the second time it tries to open outlook but gets errors such as Outlook not available etc. So Outlook is taking to long to do the task while the application is trying to create the object again. I was using DoEvents previously but that doesn't work. Is there anyway to wait for outlook to finish its job Before it continues?
In this scenario outlook is not open yet, it is closed and the vb6 application is opening it.
You can trap Err.Number or Err.Description and generate a message box for the user to click to try again (Resume) or cancel (Exit Sub).
You may loop continually, attempting to create, to avoid user intervention. At some point generate the message box so users know the app is still working.
Edit 2015 05 06 - Maybe something less abstract. VBA but should be generic enough for other languages.
Option Explicit
Private Sub errorHandler_429()
Dim uErrorMsg1 As String
Dim uErrorMsg As String
Dim errCount As Long
uErrorMsg1 = "Click OK to try again."
On Error GoTo ErrorHandler
restart:
' code that triggers an error here
Err.Raise 429 ' <-- For testing
'Err.Raise 430 ' <--- For testing
ExitRoutine:
Exit Sub
ErrorHandler:
Select Case Err.Number
Case 429
errCount = errCount + 1
uErrorMsg = Err.Number & ": " & Err.Description & " occurred " & errCount & " times."
Debug.Print uErrorMsg
If (errCount Mod 200) = 0 Then
uErrorMsg = uErrorMsg1 & vbCr & vbCr & _
"Error # " & Err.Number & " was generated by " & _
Err.Source & Chr(13) & Chr(13) & Err.Description
Debug.Print uErrorMsg
If MsgBox(uErrorMsg, vbOKCancel, "errorHandler_429", Err.HelpFile, Err.HelpContext) = vbOK Then
Resume restart
Else
Resume ExitRoutine
End If
Else
Resume restart
End If
Case Else
uErrorMsg = Err.Number & ": " & Err.Description
'Debug.Print uErrorMsg
MsgBox uErrorMsg, , "errHandler_429", Err.HelpFile, Err.HelpContext
Resume ExitRoutine
End Select
End Sub
Sub errHandler_Description()
' Where the error number is negative and inconsistent
Dim uErrorMsg1 As String
Dim uErrorMsg As String
Dim errCount As Long
Dim LErrDesc As String
uErrorMsg1 = "Click OK to try again."
On Error GoTo ErrorHandler
restart:
' code that triggers an error here
Err.Raise 429 ' <--- For testing
'Err.Raise 430 ' <--- For testing
ExitRoutine:
Exit Sub
ErrorHandler:
LErrDesc = Left(Err.Description, 51)
Debug.Print " LErrDesc: " & LErrDesc
Select Case LErrDesc
Case "ActiveX component can't create object"
errCount = errCount + 1
Debug.Print " errCcount: " & errCount
If (errCount Mod 200) = 0 Then
uErrorMsg = uErrorMsg1 & vbCr & vbCr & _
"Error # " & Err.Number & " was generated by " & _
Err.Source & Chr(13) & Chr(13) & Err.Description
'Debug.Print uErrorMsg
If MsgBox(uErrorMsg, vbOKCancel, "errHandler_Description", Err.HelpFile, Err.HelpContext) = vbOK Then
Resume restart
Else
Resume ExitRoutine
End If
Else
Resume restart
End If
Case Else
uErrorMsg = "This error has not been handled."
uErrorMsg = uErrorMsg & vbCr & vbCr & _
"Error # " & Err.Number & " was generated by " & _
Err.Source & Chr(13) & Chr(13) & Err.Description
Debug.Print uErrorMsg
MsgBox uErrorMsg, , "errHandler_Description", Err.HelpFile, Err.HelpContext
Resume ExitRoutine
End Select
End Sub

ADODB error handling variable not setting

I building an ADODB error trap but for some reason, by errSQL.Number and errSQL.Description both give me a "Object variable or With Block variable not set." error....here is my code so far...I have the active x object enabled and I thought that .number and .description are correct...any help would be awesome! The query I'm running also purposely will send an error.
When I comment the error trap out, I do get a message box with a SQL syntax error but can't seem to trap it like below...
Public errSQL As ADODB.Error
Public strErrODBC As String
Private Sub verifySQL()
Dim strSQL2 As String
Dim cn As New ADODB.Connection
Dim cdTxt As String
Dim rs As New ADODB.Recordset
Dim intVerify As Integer
On Error GoTo ODBCErrorHandler
cn.ConnectionString = "DSN=source;"
cn.Open
If cn.State = adStateOpen Then
rs.Open "SELECT CASE WHEN MAX((CASE WHEN " & Forms!dlgSplitName.lstbxFlds.Column(0) & " " & cdTxt & " THEN 1 ELSE 0 END)) =1 THEN 1 ELSE 0 END FROM table;", cn
Else
End If
intVerify = rs.Fields(0).Value
If intVerify = 1 Then
insrt_Test
ElseIf intVerify = 0 Then
MsgBox "No records were found with the code text logic.", vbExclamation + vbOKOnly, "Spliy by Notification"
End If
ODBCErrorHandler:
Debug.Print errSQL.Number
Debug.Print errSQL.Description
strErrODBC = "A SQL error was encountered with the code text logic." & vbCrLf
strErrODBC = strErrODBC & "Error " & errSQL.Number & vbCrLf
strErrODBC = strErrODBC & " " & errSQL.Description
MsgBox strErrODBC & vbCrLf & "Please try again.", vbCritical, "Split by field code text error."
cn.Close
End Sub
The problem is that the errSQL ADODB Error object is never set to anything. the Connection object has an error collection that you need to use to display the errors. Try this:
ODBCErrorHandler:
Dim ErrorCount As Double
Dim strError As String
ErrorCount = cn.Errors.Count
strErrODBC = "A SQL error was encountered with the code text logic." & vbCrLf
If ErrorCount > 0 Then
For index = 0 To (ErrorCount - 1)
Set errSQL = cn.Errors.Item(index)
strErrODBC = strErrODBC & "Error " & errSQL.Number & vbCrLf
strErrODBC = strErrODBC & " " & errSQL.Description & vbCrLf
Next index
End If
MsgBox strErrODBC & vbCrLf & "Please try again.", vbCritical, "Split by field code text error."
cn.Close
Hope this helps.