0

I am working on converting our multiple access (2016) files / databases into one consolidated access file with navigation, and the data hosted on a SQL server (2014). Currently, we have a button that has the below code, and every time I get to an s.update line, I get the error "new transaction is not allowed because there are other threads running in the session".

I've been Googling for a day or 2 now and can't seem to get rid of it. I read that enabling MARS could help since I have 2 recordsets open, but that did not help. I do see the "MARS_Connection=Yes" on that tables connection string. I've also read that for loops can cause issues but none of the s.update lines are actually in a for loop. I've had trouble finding this issue in relation to Access

I'm relearning VBA as I go, I did not write this app and am open for suggestions.

Private Sub cmdNewWeek_Click()
    On Error GoTo ErrorHandler
    Dim r As DAO.Recordset, s As DAO.Recordset, f As Field, DifferentDate As Boolean, d As Date
    d = Date - (Weekday(Date) - 2)

    If IsNull(Me.cboSelAtty) Then
        MsgBox "Select an attorney first."
        cboSelAtty.SetFocus
    Else
        If IsNull(Me.employee) Then Me.employee = Me.cboSelAtty
        DoCmd.RunCommand acCmdSaveRecord
        DifferentDate = False
        MsgBox cboSelAtty
        Set r = CurrentDb.OpenRecordset("Select top 1 * From kt_workload Where employee=" & CSql(cboSelAtty) & " Order By week Desc", dbOpenSnapshot)
        Set s = CurrentDb.OpenRecordset("kt_workload", dbOpenDynaset, dbSeeChanges)
        If r.EOF Then
            s.AddNew
            s("employee") = cboSelAtty
            s("week") = d
            s.Update
            s.Close
            r.Close
            Me.Requery
            Exit Sub
        ElseIf r("week") >= d Then
            If MsgBox("A record for this week already exists. Do you want to enter one for a different week?", vbCritical + vbYesNo) = vbNo Then
                r.Close
                Exit Sub
            Else
                DifferentDate = True
            End If
        End If
        s.AddNew
        For Each f In r.Fields
            If f.Name <> "week" Then s(f.Name) = r(f.Name)
        Next
        s("week") = IIf(DifferentDate, r("week") + 7, d)
        s.Update
        s.Close
        r.Close
        Me.Requery
    End If
ErrorHandler:
    'Start ODBC error Catch
    Dim i As Integer
    Dim st As String
    For i = 0 To Errors.Count - 1
        st = st & Errors(i).Description & vbCrLf
    Next i
    MsgBox st, vbCritical
    'End ODBC error Catch
End Sub

Example Data (I couldn't get the table to format properly for whatever reason): Example Data

In the end, all we are doing is copying the most recent record as the 'test' fields are often similar week to week.

Edit: I trimmed down the function to the below. I do get 1 record back from my "r" record as expected. It gets applied to the "s" new record just fine.

but s.update throws the same error. Also, if I run this and run a SQL query through SSMS, that query hangs up until the access form throws the error (~60 seconds), so I'm not sure where I am going wrong with the SQL connection side I assume.

Trimmed Down Code:

Private Sub cmdNewWeek_Click()
    On Error GoTo ErrorHandler
    Dim r As DAO.Recordset, s As DAO.Recordset, DifferentDate As Boolean, d As Date
    d = Date - (Weekday(Date) - 2)
    Set r = CurrentDb.OpenRecordset("Select top 1 * From kt_workload Where employee=" & CSql("jcraig") & " Order By week Desc", dbOpenSnapshot)
    Set s = CurrentDb.OpenRecordset("kt_workload", dbOpenDynaset, dbSeeChanges)
    s.AddNew
    For Each f In r.Fields
        If f.Name <> "week" Then s(f.Name) = r(f.Name)
        Debug.Print s(f.Name)
    Next
    s("week") = d
    s.Update
    s.Close
    r.Close
        
ErrorHandler:
    'Start ODBC error Catch
    Dim i As Integer
    Dim st As String
    For i = 0 To Errors.Count - 1
        st = st & Errors(i).Description & vbCrLf
    Next i
    MsgBox st, vbCritical
    'End ODBC error Catch
End Sub
ry8s
  • 11
  • 3
  • I have no problem opening two recordsets from same SQLServer table and editing them and I know nothing about MARS. Looks like procedure can close recordsets in an If Then structure then later tries to edit recordsets even if have been closed. Have you step debugged? Edit question to provide sample data as text tables. – June7 Nov 09 '21 at 04:39
  • 1
    `r` does not need to support writes. Open it with `dbOpenSnapshot`, having the same table open twice for editing with interactive updates on the same connection is not supported afaik. – Erik A Nov 09 '21 at 11:31
  • @june7 - I have debugged, and the logic looks fine, anytime we close r we end the sub, so it should not be closed before we need it closed. – ry8s Nov 09 '21 at 16:47
  • @ErikA thats a good idea, I did update it to be dbOpenSnapshot, but I get the same errors, and now access hangs up for about a minute or so :( – ry8s Nov 09 '21 at 16:49
  • If you still have `dbSeeChanges` with `dbOpenSnapshot`, remove it. -- `dbOpenSnapshot` may want to read all records, so if your table is large, it will take time. Make sure to use `TOP 1`. – Andre Nov 09 '21 at 16:56
  • @Andre I did remove dbSeeChanges and added select top 1 but it still seems to hang and I get the same errors – ry8s Nov 09 '21 at 17:14
  • Where exactly does it hang? -- If your form is bound to the same table, it may interfere. Create a standalone test copy of your function and run it without the form (provide a constant value for cboSelAtty). – Andre Nov 09 '21 at 17:26
  • @Andre the form is indeed bound to the same table - its anytime I try to run S.update, the form hangs for ~60s and I assume timeouts somewhere with the same error "new transaction is not allowed because there are other threads running in the session" I will create a test copy and see if that helps – ry8s Nov 09 '21 at 17:56
  • Sorry, I missed the Exit Sub lines. Again, I cannot replicate issue. I tested code with bound form open, two recordsets of same table and adding record into one recordset with data from other. Tested code behind form and in general module. – June7 Nov 09 '21 at 19:22
  • Thank you @June7 - I do think its a specific issue with my SQL connection after further debugging.. Now to figure out what.. – ry8s Nov 09 '21 at 20:06
  • I should add I am running SQLServer Express on a laptop so connection is localhost. I have allowed this PC to be shared but not figured out how to get other PC to interact with the SQLServer db, if it is even possible. – June7 Nov 09 '21 at 20:16
  • @June7 We have Full SQL on a network server. Other forms and operations work, so it has the connection, and it can pull back data and save other records, but for some reason it does not like saving it through the result set it seems. – ry8s Nov 09 '21 at 20:24
  • 1
    So are you not able to save via recordset ever? Did you try a simple procedure of opening a single recordset and doing AddNew and/or Edit a single field? No form open. You can always delete bogus record. – June7 Nov 09 '21 at 20:28
  • I can't see what you are doing so that you can't get an exclusive lock on the table, to add the new auto-key identifier value, but I wonder if it would be allowed if you had only one thread instead of 3? https://documentation.help/MS-Jet-SQL/dahowchangingjetengine40.htm – david Nov 10 '21 at 02:21

2 Answers2

1

In the end, I think this is some connection issue unique probably to access 2016, SQL Server 2014 and ODBC 17 drivers. Instead of doing the double recordsource, I'm opening up the read recordsource and just using the values to insert a new record, and then I'll just select this new record. It works now at least.

  sql = "INSERT INTO kt_workload (employee, week, availweek, availMonth, availQtr, activeWeek, activeMonth, activeQtr) VALUES (" & _
          CSql(r("employee")) & ",'" & _
          r("week") & "'," & _
          CSql(r("availweek")) & "," & _
          CSql(r("availMonth")) & "," & _
          CSql(r("availQtr")) & "," & _
          CSql(r("activeWeek")) & "," & _
          CSql(r("activeMonth")) & "," & _
          CSql(r("activeQtr")) & _
          ");"
Debug.Print sql
CurrentDb.Execute sql
ry8s
  • 11
  • 3
0

Whenever you need a recordset to only read data from, you should open it as snapshot.

In your case you only want to read the first record, so you should also use TOP 1.

Should you ever have an attorney named O'Brien, your code will break. Use Gustav's CSql() function when concatenating variables with SQL. It handles all strings and prevents SQL injection.

In summary:

strSql = "Select TOP 1 * From table1 Where employee=" & CSql(cboSelAtty) & " Order By week Desc"
Set r = CurrentDb.OpenRecordset(strSql, dbOpenSnapshot)

This way you won't have conflicting transactions.

Note that your ODBC error handling loop should go into an On Error Goto xxx handler.

Andre
  • 26,751
  • 7
  • 36
  • 80
  • I updated r(read recordset) that to be dbOpenSnapshot without dbSeeChanges, and made it select top 1. Thankfully we do the lookup based on usernames which will have all ' stripped (because I already ran into that issue!) and I added the CSql function (thanks for that!). I moved the error handling down to On Error (it was temporary so I can get a more indepth description of the ODBC error) I'm still having the same errors though – ry8s Nov 09 '21 at 17:26