0

I'm getting the following error when trying to delete a table:

Run-time error '3211'

The database engine could not lock table 'RuleViolations1516' because it is already in use by another person or process.

Here is the offending procedure, with a comment showing the line throwing the error:

Public Sub ImportRuleViolations()

    DoCmd.Close acForm, "frmImportRuleViolations"

    If _
        TableExists("RuleViolations1516") = True _
    Then

        Debug.Print "Table RuleViolations1516 already exists"

        DoCmd.DeleteObject acTable, "RuleViolations1516" ' <-- EXECUTION STOPS HERE
        Debug.Print "...old table deleted..."

        DoCmd.TransferSpreadsheet acTable, _
                  10, _
                  "RuleViolations1516", _
                  Forms!frmImportRuleViolations.txtRuleViolationsPath & Forms!frmImportRuleViolations.txtRuleViolationFile, _
                  -1
        Debug.Print "...new data imported."

    ElseIf _
        TableExists("RuleViolations1516") = False _
    Then

        Debug.Print "Table RuleViolations1516 does not already exist"

        DoCmd.TransferSpreadsheet acTable, _
                          10, _
                          "RuleViolations1516", _
                          Forms!frmImportRuleViolations.txtRuleViolationsPath & Forms!frmImportRuleViolations.txtRuleViolationFile, _
                          -1
        Debug.Print "...new data imported."

    End If

    Dim db As DAO.Database
    Dim tDef As TableDef, fld As DAO.Field

    Set db = CurrentDb

    db.TableDefs.Refresh

    ' LRN

        Set tDef = db.TableDefs("RuleViolations1516")
        Set fld = tDef.CreateField("newLRN", dbText, 20)
        fld.OrdinalPosition = 2
        tDef.Fields.Append fld

        db.Execute _
            "UPDATE RuleViolations1516 Set newLRN=[Learner Ref]", dbFailOnError

        ' delete old field
        tDef.Fields.Delete "Learner Ref"
        tDef.Fields.Refresh

        ' rename new field
        tDef.Fields("newLRN").name = "LRN"
        tDef.Fields.Refresh

        Set fld = Nothing
        Set tDef = Nothing

    ' AimRef

        Set tDef = db.TableDefs("RuleViolations1516")
        Set fld = tDef.CreateField("newAimRef", dbText, 20)
        fld.OrdinalPosition = 7
        tDef.Fields.Append fld

        db.Execute _
            "UPDATE RuleViolations1516 Set newAimRef=[Aim Reference Number]", dbFailOnError

        ' delete old field
        tDef.Fields.Delete "Aim Reference Number"
        tDef.Fields.Refresh

        ' rename new field
        tDef.Fields("newAimRef").name = "AimRef"
        tDef.Fields.Refresh

        Set fld = Nothing
        Set tDef = Nothing

    Set db = Nothing

    DoCmd.OpenForm "frmImportRuleViolations"

End Sub

The offending sub also makes reference to another function:

Public Function TableExists(name As String) As Boolean

    TableExists = DCount("*", "MSysObjects", "Name = '" & name & "' AND Type = 1")

End Function

The above sub and function run on their own separate module (not tied to a form module).

The table RuleViolations1516 is not open when I run the sub. The form frmImportRuleViolations uses the RuleViolations1516 table in some queries behind some subforms, but as you can see from the sub, I've closed this form on the first line.

Any pointers would be appreciated.

Update:

frmImportRuleViolations has 2 subforms on it... removing them (temporarily) stops the issue. I need the subforms on the form though.. how can I get around this?

Matt Hall
  • 2,412
  • 7
  • 38
  • 62
  • What happens if you do the `DoCmd.DeleteObject` before the if statement? Does it fail there as well? – BIBD Sep 03 '15 at 19:32
  • As a test only, if you remove or disable `DoCmd.Close acForm, "frmImportRuleViolations"` and then run *ImportRuleViolations* when *frmImportRuleViolations* is closed, do you still get the same error? – HansUp Sep 03 '15 at 20:40
  • @BIBD I commented-out all code in that sub except the `DoCmd.DeleteObject` line. Same error occurs. – Matt Hall Sep 04 '15 at 08:16
  • @HansUp I created a close button for the form with the `DoCmd.Close acForm, "frmImportRuleViolations"` line on its click event. I then made the call to the ImportRuleViolations sub on the form's close event (hope this is what you meant). Same error occurs. – Matt Hall Sep 04 '15 at 08:21
  • I've added an update to my question.. it seems the subforms on my main form are causing the issue... not sure how to get around this though. – Matt Hall Sep 04 '15 at 09:11
  • Is whatever is triggering `ImportRuleViolations` on that same form `frmImportRuleViolations`? – BIBD Sep 04 '15 at 17:23

1 Answers1

1

As I understand your updated question, you have a form called frmImportRuleViolations and that has a subform I'll call frmImportRuleViolationsSubform1.

frmImportRuleViolationsSubform1 references the RuleViolations1516 table somewhere on it.

I'm assuming you are also triggering this reload from a button or other control on frmImportRuleViolations. If so, then that makes sense as the cause of your error. Access may not have given up every reference to RuleViolations1516 by the time it gets to the close event.

That and a form closing and reopening itself can get kind of hairy.

One fairly easy thing to do would be to drop and load the table before the frmImportRuleViolationsSubform1 is opened. As a user (and a developer) that's what I would expect - I open the form and it's right up to date. And worst case situation closing the form and re-opening it gives me a refresh.

Failing that (it absolutely has to be reloaded after the form is closed); then I would make whatever is closing it open frmImportRuleViolations as a modal window, and then put reload of the table code after the call to frmImportRuleViolations since it will wait until control is returned to the calling window.

You could also instead load the data into a temporary staging table, and then delete the contents of RuleViolations1516 and repopulate it from the staging table. This would probably be the route I would take as it would eliminate the closing and opening of the frmImportRuleViolations form. You'd just have to tell the table on the sub-form to refresh (which I'll leave an an exercise for the reader).


Aside from that, a couple style notes:

'' this 
If _
    TableExists("RuleViolations1516") = True _
Then

'' could be equally written as
If TableExists("RuleViolations1516") Then
'' the "= True" is implied and not required

And

'' this
ElseIf _
    TableExists("RuleViolations1516") = False _
Then

'' is redundant to the previous if. A simple else would do, since we
'' know if the last time it ran, it wasn't true, it must be false

And the DoCmd block looks like it does the same thing in both the if and else, so I'd be inclined to extract it like this:

If TableExists("RuleViolations1516") then
    Debug.Print "Table RuleViolations1516 already exists"
    DoCmd.DeleteObject acTable, "RuleViolations1516" 
    Debug.Print "...old table deleted..."
else
    Debug.Print "Table RuleViolations1516 does not already exist"
end if

DoCmd.TransferSpreadsheet acTable, _
    10, _
    "RuleViolations1516", _
    Forms!frmImportRuleViolations.txtRuleViolationsPath & _
        Forms!frmImportRuleViolations.txtRuleViolationFile, _
    -1
Debug.Print "...new data imported."

That way when you come back to change the name of the file (or whatever) you are only changing it in one place, etc. and goes along with the DRY principle - "DRY - don't repeat your self".

I like the use of underscores so that what you have coded does not run off the edge of the screen like some extract from war and peace. It makes it much more readable.

What you've done is not wrong. It's just not what most developers would do. And as much as we write code for the computer to function the way we want, we also want the next developer to touch your code 5 years down the line to easily understand what you are doing. And code always lasts longer than you think it will :^)

Good luck!

BIBD
  • 15,107
  • 25
  • 85
  • 137
  • (1/3) Thanks for all these pointers - all very useful. The close/open of the form wasn't part of the intended design, it was just something I tried to get Access to let go of the table and then I left it in my question to show how weird it is that the table was still considered open when everything using it was no longer on screen. The refresh of the data comes from a command button on the form that calls `ImportRuleViolations()`. – Matt Hall Sep 07 '15 at 09:43
  • (2/3) Unfortunately I can't completely automate the refresh as it relies on importing a spreadsheet whose file name can vary (so the user has to manually pick an .xlsx file using the file dialogue: http://i.imgur.com/wMRLsxT.jpg). I came up with the workaround of simply splitting the form, so that there's a separate import form that handles getting the new data and then opens the form with my subforms when its done. – Matt Hall Sep 07 '15 at 09:47
  • (3/3) Thanks for those style tips. I quite often write my initial code in a very explicit way until I'm happy it all works ok... hence why I tend to write `IF` statements with the implied `= True` and `= False` parts (just makes it easier for me to scan initially). Then I'll usually shrink things down later when I've got more time. You're quite right about the redundant use of `TransferSpreadsheet`; bit sloppy of me, thanks for spotting that. Even though I used a different method to resolve this I can see the suggestions you've made are sound and so I've marked as answer. Thank you. – Matt Hall Sep 07 '15 at 09:56