Update or CancelUpdate without AddNew or Edit - Access error - database

I have built a database that I get an occasional error in.
I have a bound form in split form/datasheet view. Occasionally when updating records, I get the "Update or CancelUpdate without AddNew or Edit" error when moving to a new record. This will happen in either the single record form, or on the datasheet.
It does not happen every time the record is saved. Maybe 1 out of 20 or 30 times.
I do have an AuditTrail built in, which is my only guess as to what may be causing the problem.
The VBA on the form:
Private Sub Form_AfterDelConfirm(Status As Integer)
If Status = acDeleteOK Then Call AuditChanges("ApptID", "DELETED PRIOR RECORD")
End Sub
Private Sub Form_BeforeUpdate(Cancel As Integer)
If Me.NewRecord Then
Call AuditChanges("ApptID", "NEW")
Else
Call AuditChanges("ApptID", "EDIT")
End If
End Sub
Private Sub Form_Delete(Cancel As Integer)
If Status = acDeleteOK Then Call AuditChanges("ApptID", "DELETE BUTTON HIT")
End Sub
The AuditTrail code is:
Sub AuditChanges(IDField As String, UserAction As String)
On Error GoTo AuditChanges_Err
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim ctl As Control
Dim datTimeCheck As Date
Dim strUserID As String
Set cnn = CurrentProject.Connection
Set rst = New ADODB.Recordset
rst.Open "SELECT * FROM AuditTrail", cnn, adOpenDynamic, adLockOptimistic
datTimeCheck = Now()
strUserID = Environ("USERNAME")
Select Case UserAction
Case "EDIT"
For Each ctl In Screen.ActiveControl.Parent.Controls
If ctl.Tag = "Audit" Then
If Nz(ctl.Value) <> Nz(ctl.OldValue) Then
With rst
.AddNew
![DateTime] = datTimeCheck
![UserName] = strUserID
![FormName] = Screen.ActiveControl.Parent.Name
![Action] = UserAction
![RecordID] = Screen.ActiveControl.Parent.Controls(IDField).Value
![FieldName] = ctl.ControlSource
![OldValue] = ctl.OldValue
![NewValue] = ctl.Value
.Update
End With
End If
End If
Next ctl
Case Else
With rst
.AddNew
![DateTime] = datTimeCheck
![UserName] = strUserID
![FormName] = Screen.ActiveControl.Parent.Name
![Action] = UserAction
![RecordID] = Screen.ActiveControl.Parent.Controls(IDField).Value
.Update
End With
End Select
AuditChanges_Exit:
On Error Resume Next
rst.Close
cnn.Close
Set rst = Nothing
Set cnn = Nothing
Exit Sub
AuditChanges_Err:
MsgBox Err.Description, vbCritical, "ERROR!"
Resume AuditChanges_Exit
End Sub
If the error doesn't involve the VBA code, I have no idea what the problem could be.

I concur with dbmitch; adding some extra info to your error message would be a great help.
In addition, if that doesn't get you exactly what you want, you can also implement the little-known ERL function. Most people don't even know that Access can trap at the line level if they add line numbers (are you old enough to remember Basic?) to their code.
So, something like:
Sub AuditChanges(IDField As String, UserAction As String)
10 On Error GoTo AuditChanges_Err
20 Dim cnn As ADODB.Connection
30 Dim rst As ADODB.Recordset
40 Dim ctl As Control
50 Dim datTimeCheck As Date
60 Dim strUserID As String
70 Set cnn = CurrentProject.Connection
80 Set rst = New ADODB.Recordset
etc...
And then you could change your error handler to be something like:
400 MsgBox "UserAction: " & UserAction & vbCrLf & _
"IDField: " & IDField & vbCrLf & _
"Error Line: " & Erl & vbCrLf & _
"Error: (" & Err.Number & ") " & Err.Description, vbCritical, "ERROR!"

Turns out that the problem didn't seem to have any issues with the AuditTrail code.
There is a combobox that was getting hung up occasionally when moving to a new record.
I added the code below to the 'On Exit' event for the field, and I haven't seen the error come up since.
If Me.Dirty Then
Me. Dirty = False
End If

Related

How can I solve the error for connecting to SQL Server

I am getting an error for connecting to the sql error is named pipes provider could not open a connection to SQL Server 1265. Here is the code and it worked yesterday and when I check it today it is not working and I get the error.
Here is the vb code:
'Require all variables to be defined
'to prevent rogue variables and limit
'debugging time
Option Explicit
'====================================================================================
' GLOBAL VARIABLES
'====================================================================================
Private Const g_sqlServer = "EWNVM-2017U3"
Private g_lStartDate As Long
Private g_nDaysInMonth As Integer
Public Enum mrReportType
mrDailyReport
mrMonthlyReport
mrYearlyReport
End Enum
'====================================================================================
' GetData(nYear, nMonth)
'====================================================================================
Public Sub GetData(ByVal eReportType As mrReportType, ByVal nYear As Integer, Optional ByVal nMonth As Integer = 1, Optional ByVal nDay As Integer)
' On Error GoTo ErrorHandler
Dim cMRReport As New MRReport
Dim adoConn As New ADODB.Connection
Dim adoRS As New ADODB.Recordset
Dim sSqlQuery As String
Dim sStartDateFmt As String
Dim i, k As Integer
Dim sLink As String
'Get Start Date
g_lStartDate = cMRReport.GetStartDate(nYear, nMonth, nDay)
'Write report Date to RawData sheet to use on other sheets
RawData.Range("A1") = Format(g_lStartDate, "mm/yyyy")
'Show Progress Bar Form
cMRReport.ShowProgressBar
'===========================================================================================================================================
'Historian Database Queries
'===========================================================================================================================================
adoConn.ConnectionString = "Provider='SQLNCLI11';Data Source='" & g_sqlServer & "';Initial Catalog='MR_Carrolton_DB';User ID='mrsystems';Password='Reggie#123';"
adoConn.CursorLocation = adUseClient
adoConn.Open
'Daily Report Type
RawData.Range("B4", "AZ39").ClearContents
cMRReport.SetHeader Sheet2, Positioncenter, "Monthly WAS Tank Blower Runtimes Report" & vbCr & Format(g_lStartDate, "mmmm yyyy")
cMRReport.SetHeader Sheet2, PositionRight, "Pee Dee River WWTP" & vbCr & "City of Florence, SC"
QueryRuntimesDaily adoConn, adoRS, cMRReport
'Close Historian DB Connection
adoConn.Close
'-------------------------------------------------------------------------------------------------------------------------------------------
'Cleanup memory by closing
'classes we initialized
Set adoRS = Nothing
Set adoConn = Nothing
Set cMRReport = Nothing
Exit Sub
ErrorHandler:
'Clean Up
If Not adoConn Is Nothing Then
If adoConn.State = adStateOpen Then adoConn.Close
End If
Set adoConn = Nothing
cMRReport.HandleError err, "Report", "GetData"
End Sub
'===========================================================================================================================================
'Historian Database Queries Functions
'===========================================================================================================================================
'-----------------------------------------
'Query for Flow Totals Daily
'-----------------------------------------
Private Sub QueryRuntimesDaily(ByVal adoConn As ADODB.Connection, ByRef adoRS As ADODB.Recordset, cMRReport As MRReport)
' On Error GoTo ErrorHandler
Dim sSqlQuery As String
Dim i As Integer
Dim startDateSerial
Dim endDateSerial
startDateSerial = CDec(DateAdd("n", 1 * i, g_lStartDate))
' MsgBox startDateSerial
endDateSerial = CDec(DateAdd("n", 1 * i + 15, g_lStartDate))
' MsgBox endDateSerial
For i = 0 To 95
' sSqlQuery = "SELECT LogDateTime, CL2_RESIDUAL,ZW1_TURBIDITY,ZW2_TURBIDITY,ZW3_TURBIDITY,ZW4_TURBIDITY FROM MR_Carrolton_DB.dbo.DailyRuntimes ORDER BY LogDateTime"
sSqlQuery = "SELECT LogDateTime, CL2_RESIDUAL " & _
" FROM MR_Carrolton_DB.dbo.DailyRuntimes" & _
" WHERE LogDateTime >= " & startDateSerial & _
" AND LogDateTime < " & endDateSerial & _
" ORDER BY LogDateTime"
'Copy sSqlQuery value to RawData worksheet for troubleshooting
RawData.Range("B2").Value = sSqlQuery
'Open recordset (executes SQL query)
adoRS.Open sSqlQuery, adoConn, 0, 1, 1
'If recordset is not empty then copy data to raw sheet
If adoRS.BOF = False And adoRS.EOF = False Then
RawData.Cells((i + 4), 2).CopyFromRecordset adoRS
End If
'Close recordset after each query
adoRS.Close
'Update Progress Bar
cMRReport.UpdateProgressBar i, 96, "Querying for Daily Runtimes..."
'Prevent VBA from locking up Excel
'while running through loops
DoEvents
Next i
Exit Sub
ErrorHandler:
'Clean Up
If Not adoConn Is Nothing Then
If adoConn.State = adStateOpen Then adoConn.Close
End If
Set adoConn = Nothing
cMRReport.HandleError err, "Report", "QueryRuntimesMonthly"
End Sub
'-----------------------------------------
' Lock/Unlock Worksheets
'-----------------------------------------
Public Sub LockWorksheets()
Dim ws As Worksheet
Dim i As Integer
For Each ws In Worksheets
ws.Protect "reggie"
Next
End Sub
Public Sub UnLockWorksheets()
Dim ws As Worksheet
Dim i As Integer
For Each ws In Worksheets
ws.Unprotect "reggie"
Next
End Sub
it seems it is more of server administration issue than a coding one. Ping your server to check if there is connectivity problem. Your connection does not persist so you must check your "hosts" file and Sql Server settings if they are properly set.
Visit this page for step by step troubleshooting:
Resolving could not open a connection to sql server errors

VBA worksheet_change limited to single column doesn't respond to automatic updates

I am building a patient database. I have code that checks for changes in a specific column. if data in that column reaches a certain range, i make it send an email. Currently when i manually update the column the program works flawlessly, but when i have a date based formula update it - the macro doesn't seem to recognize it.
What could the problem be?
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column <> 22 Then Exit Sub
Dim rng As Range
For Each rng In Range("V1:V14")
If (rng.Value < 5 And rng.Value > 1) Then
Call mymacro(rng.Address)
End If
Next rng
End Sub
Private Sub mymacro(theValue As String)
Dim xOutApp As Object
Dim xOutMail As Object
Dim xMailBody As String
Set xOutApp = CreateObject("Outlook.Application")
Set xOutMail = xOutApp.CreateItem(0)
xMailBody = "Hi there" & vbNewLine & vbNewLine & _
"The patient that is due is in cell: " & theValue
On Error Resume Next
With xOutMail
.To = "email#hotmail.com"
.CC = ""
.BCC = ""
.Subject = "Upcoming Patient"
.Body = xMailBody
.Display 'or use .Send
End With
On Error GoTo 0
Set xOutMail = Nothing
Set xOutApp = Nothing
End Sub
the formula that updates the column is
=IF(P7<>"",(P7-TODAY()),"")

Execute SQL Server stored procedure from VBA and retrieve all messages and result sets

I want to be able to execute a SQL Server stored procedure from MS Access VBA, in such a way that I can read (1) all the resulting result sets, not just the first one; and (2) any messages produced by PRINT statements or similar.
I have a test stored procedure with one input parameter, which produces 3 distinct result sets and about 90 messages. It calls several sub-stored procedures, I can EXEC it perfectly well from SSMS, but it isn’t clear (to me) how best to do it from Access VBA. I have tried the following so far:
DAO. Using SQL pass-through queries, I can get a lot of what I want in DAO, though it is a little clunky. It returns the first of the 3 result sets as a recordset, and by using the LogMessages attribute I can get a table (“Admin – NN”) containing the emitted messages.
ADO. Using Connection and Command objects, I can obtain a single recordset representing the first result set from the stored procedure. However, I can’t seem to persuade it to produce anything but a forward-only recordset. Regarding messages, at one point, all of them (at least, the first 127 of the approx. 150 I expected) were going into the connection’s Errors collection (!), but when I cut the number down to about 90, none of them appeared anywhere at all that I could find.
What I really want, as I said at first, is the output from all result sets, plus the messages. Is this possible?
Here is a listing of the routine I am currently using for executing a stored procedure :
Function ExecuteStoredProcedureADO(SPName As String, Connect As String, ReturnsRecords As Boolean, _
ParamArray Params() As Variant) As ADODB.Recordset
' v1.0 2018/06/26
' execute stored procedure SPName on a SQL Server database specified by the string in Connect
Dim strErr As String
Dim i As Integer
Dim lngRecsAffected As Long
Dim cnn As ADODB.Connection
Dim cmd As ADODB.Command
Dim errCurr As ADODB.Error
Dim rst As ADODB.Recordset
On Error GoTo Catch
Set ExecuteStoredProcedureADO = Nothing
Set cnn = New ADODB.Connection
cnn.Errors.Clear
cnn.mode = adModeRead
cnn.CommandTimeout = 300
cnn.Open Connect
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = cnn
.CommandText = SPName
.CommandType = adCmdStoredProc
For i = 0 To UBound(Params) Step 4
.Parameters.Append .CreateParameter(Params(i), Params(i + 1), adParamInput, Params(i + 2), Params(i + 3))
Next i
Set rst = New ADODB.Recordset
rst.CursorType = adOpenStatic
If ReturnsRecords Then
'''Set rst = .Execute(lngRecsAffected)
rst.Open cmd, , adOpenStatic, adLockReadOnly
Else
Set rst = .Execute(, , adExecuteNoRecords)
End If
End With
If ReturnsRecords Then Set ExecuteStoredProcedureADO = rst
Final:
On Error Resume Next
If Len(strErr) > 0 Then Call AppendMsg(strErr)
Set rst = Nothing
Set cmd = Nothing
Exit Function
Catch:
If cnn.Errors.Count > 0 Then
With cnn
For Each errCurr In cnn.Errors
strErr = strErr & "Error " & errCurr.Number & ": " & errCurr.Description _
& " (" & errCurr.Source & ")" & vbCrLf
Next errCurr
strErr = Left(strErr, Len(strErr) - 2) ' truncate final CRLF
End With
Else
strErr = "Error " & Err.Number & ": " & Err.Description & " (" & Err.Source & ")"
End If
MsgBox strErr, vbOKOnly, gtitle
Resume Final
End Function
Addendum: Regarding the multiple result sets, I am hoping that http://msdn.microsoft.com/en-us/library/ms677569%28VS.85%29.aspx
will be of some help.
To shamelessly piggy-back off of #Erik, you want to create a new class that will handle your processing. Something like cProcedureHandler. Within this class, you need to declare an ADODB.Connection object using the WithEvents keyword:
Dim WithEvents cn As ADODB.Connection
Then, you need to write a InfoMessage event handler that will take care of the multiple print statements. Information about the InfoMessage event can be found here, and using the connection's Errors collection can be found here. So you'll end up with something like this:
Private Sub cn_InfoMessage(ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
Dim err As ADODB.Error
Debug.Print cn.Errors.Count & " errors"
For Each err In cn.Errors
' handle each error/message the way you need to.
Debug.Print err.Description
Next err
End Sub
Since you've taken care of the code to handle multiple messages, now you just need to handle the multiple recordsets, which is explained pretty well in the link you provided. One thing I noticed was that the example link used rs is nothing as the check for when there were no more recordsets, which didn't work for me. I had to use the rs State property. So I ended up with this:
Public Sub testProcedure()
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset
Dim recordSetIndex As Integer
Set cn = modData.getConnection
Set cmd = New ADODB.Command
cmd.ActiveConnection = cn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "dbo.sp_foo"
Set rs = cmd.Execute
recordSetIndex = 1
Do Until rs.State = ObjectStateEnum.adStateClosed
Debug.Print "contents of rs #" & recordIndex
Do Until rs.EOF
Debug.Print rs.Fields(0) & rs.Fields(1)
rs.MoveNext
Loop
Set rs = rs.NextRecordset
recordSetIndex = recordIndex + 1
Loop
cn.Close
Set rs = Nothing
Set cn = Nothing
Set cmd = Nothing
End Sub
Then, when you're ready to run your SP from VBA, just do something like this:
set obj = new cProcedureHandler
obj.testFooProcedure
Another thing (you probably have already done this): Make sure your actual stored procedure in SQL Server sets nocount on.

Change the text in a field with a VBA function

I'm currently working on a database in Access which has a table (Pro_Cons) who compares the pros and cons of different product-types. This table has 3 columns; Type (Product_type), Pros (Pro) and Cons.
For each of product-type I created a form which includes the pro- and the cons-field of the according type out of this table.
For adding new text to this fields I'm creating a VBA-function which is triggered by a button-click.
Because nothing worked as supposed to, I created the following function, which should only replace the Pro -field of the product type1 to change1.
Access doesnt throw an error, but nothing changes in the table.
Has someone an idea whats happening here?
Sub Change_Pos_Inf()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim strSQL As String
Dim strSensortyp As String
Dim strNew As String
Set db = CurrentDb()
strProduct_type = "type1"
strNew = "change1"
strSQL = "SELECT Pro FROM Pro_Cons WHERE Product_type='strProduct_type';"
Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)
With rst
If .RecordCount > 0 Then
.MoveFirst
.Edit
!Pro.Value = strNew
.Update
End If
End With
End Sub
I think your literal should be changed to reference a form control:
strSQL = "SELECT Pro FROM Pro_Cons WHERE Product_type='strProduct_type';"
Changes to:
strSQL = "SELECT Pro FROM Pro_Cons WHERE Product_type='" & Me!strProduct_type &
"'";
Please try the following code. It corrects your WHERE clause:
Sub Change_Pos_Inf()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim strSQL As String
Dim strSensortyp As String
Dim strNew As String
Set db = CurrentDb()
strProduct_type = "type1"
strNew = "change1"
strSQL = "SELECT Pro FROM Pro_Cons WHERE Product_type= '" & strProduct_type & "';"
Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)
With rst
If Not rst.EOF Then
.Edit
!Pro.Value = strNew
.Update
Else
MsgBox "No Record found!!"
End If
End With
rst.Close
Set rst = Nothing
End Sub

Excel VBA Recordset is empty

i have an ADODB recordset to load data from a view in an Excel sheet. The view is located in a MS SQL database. It runs perfectly for months, but since a few days the recordset is always empty, so i did not get any results. After a long day searching the www for any reasons i found, that it can happen because i use a x32 Excel and there is so much data in the view. So i separated the procedure in two queries. This helped a lot and the macro run perfectly again. Yesterday the same error appears again, so i began to split the procedure again. But now the recordset is still empty. I don't know any reason for this. I tested it with a selection of only ten rows of the view and this runs but if i want to get 1000 rows the recordset is empty again. Does anybody know a reason for this problem? All queries give the result i want to have in the database so they are ok.
Here is my code:
Sub doStuff()
Dim sqlStatement(9) As String
Dim lrow As Long
'... other variables
sqlStatement(1) = "Select * from db.View1 where location like 'forest'"
'... other sqlStatements
For i = 0 To UBound(sqlStatement)
Call loadData(sqlStatement, lrow)
Next i
End Sub
Sub loadData(sqlStatement As String, lastrow As Long)
Dim sqlServer As String
Dim dbName As String
sqlServer = "MSSQLSERVER"
dbName = "database"
On Error Resume Next
Dim con As Object
Set con = CreateObject("ADODB.Connection")
con.Open _
"Provider = sqloledb;" & _
"Data Source=" & sqlServer & ";" & _
"Initial Catalog=" & dbName & ";" & _
"User ID=user1;" & _
"Password=abcde;"
Dim rst As Object
Set rst = CreateObject("ADODB.Recordset")
With rst
.ActiveConnection = con
.Open sqlStatement , con, adLockReadOnly
ws.Activate
If lastrow = 1 Then
For col = 0 To .Fields.Count - 1
ws.Cells(1, col + 1).Value = .Fields(col).Name
Next
End If
ws.Activate
ws.Cells(lastrow+1,1).CopyFromRecordset rst
.Close
End With
con.Close
Set rst = Nothing
Set con = Nothing
End Sub

Resources