Без кода сложно сказать, но если вы используете DAO, вам нужно очистить свои объекты кода. Это означает установку Nothing для объектов вашей базы данных, а закрытие и установку Nothing для любых объектов набора записей.
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = DBEngine.OpenDatabase("[path to database]")
Set rs = db.OpenRecordset("[SELECT statement]")
rs.Close
Set rs = Nothing
db.Execute("[DML or DDL statement]", dbFailOnError)
db.Close
Set db = Nothing
Set db =CurrentDB
Set rs = db.OpenRecordset("[SELECT statement]")
rs.Close
Set rs = Nothing
Set db = Nothing ' you don't close a db variable initialized with CurrentDB
Несмотря на то, что VBA должен очищать эти объекты, когда они выходят из области видимости, он не на 100% надежен (поскольку VBA использует подсчет ссылок для отслеживания возможности освобождения объекта, и он не всегда знает, когда все ссылки были очищены).
Объекты, оставленные открытыми, являются наиболее вероятным источником блокировок, поэтому вам следует убедиться, что вы очищаете переменные объектов после того, как поработали с ними.
РЕДАКТИРОВАТЬ, увидев, что вы используете DoCmd.RunSQL:
Использование DoCmd.RunSQL, вероятно, является причиной проблемы. Это, безусловно, то, что отнимает у вас программное управление вашими подключениями. Если вы вместо этого будете использовать DAO, вы будете иметь контроль над соединением, а также сможете избежать реальной ошибки DoCmd.RunSQL, заключающейся в том, что он не обрабатывает ошибки. Если оператор DML или DDL не может завершиться успешно полностью, все должно завершиться неудачей. Например, если вы добавляете 100 записей и 10 из них терпят неудачу из-за нарушения ключа, DoCmd.RunSQL будет прозрачно добавлять 90 и НЕ ОТЧЕТАТЬ О 10 СБОЯХ. То же самое касается обновлений и любых других операторов DML / DDL. DoCmd.RunSQL «услужливо» молча завершает столько обновлений, сколько может, и вы даже не подозреваете, что некоторые из них не были завершены.
Конечно, в некоторых случаях вы можете захотеть, чтобы это произошло, например, если вы добавляете записи, которые, как вы знаете, могут иметь коллизии PK, и не хотите тратить циклы ЦП на внешнее соединение, которое устраняет дубликаты из набор записей, которые вы добавляете.
Но в большинстве случаев это не так.
Как я уже говорил в своем комментарии выше, я использую функцию, которая предназначена для прозрачной замены DoCmd.RunSQL и использует оператор DAO Execute и обработку ошибок. Я опубликовал его пару раз на SO (, вот один ), и вот версия, которую я использую в производственном процессе в моем наиболее активном на данный момент проекте разработки:
Public Function SQLRun(strSQL As String, Optional db As Database, _
Optional lngRecordsAffected As Long) As Long
On Error GoTo errHandler
Dim bolCleanup As Boolean
If db Is Nothing Then
Set db = CurrentDb
bolCleanup = True
End If
'DBEngine.Workspaces(0).BeginTrans
db.Execute strSQL, dbFailOnError
lngRecordsAffected = db.RecordsAffected
'DBEngine.Workspaces(0).CommitTrans
exitRoutine:
If bolCleanup Then
Set db = Nothing
End If
SQLRun = lngRecordsAffected
'Debug.Print strSQL
Exit Function
errHandler:
MsgBox "There was an error executing your SQL string: " _
& vbCrLf & vbCrLf & Err.Number & ": " & Err.Description, _
vbExclamation, "Error in SQLRun()"
Debug.Print "SQL Error: " & strSQL
'DBEngine.Workspaces(0).Rollback
Resume exitRoutine
End Function
(транзакции закомментированы, потому что они вызывали проблемы, которые я не успел устранить)
Вы можете заменить следующие строки:
DoCmd.RunSQL "DELETE FROM tempExtract"
DoCmd.RunSQL "INSERT INTO tempExtract SELECT (" _
& DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"
... с этим:
SQLRun "DELETE FROM tempExtract"
SQLRun "INSERT INTO tempExtract SELECT (" _
& DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"
Вы также можете сделать это:
Debug.Print SQLRun("DELETE FROM tempExtract") & " records deleted."
Debug.Print SQLRun("INSERT INTO tempExtract SELECT (" _
& DLookup("Value", "CONFIG_t", "Item = 'Xargs'" _
& ") FROM tempExtractID") & " records inserted."
Поскольку функция возвращает .RecordsActed для каждого Execute, вы можете распечатать в Immediate Window, или вы можете присвоить возвращаемое значение переменной или передать ей существующую переменную и работать с этой переменной следующим образом:
Dim lngRecordsAffected As Long
...
Call SQLRun("DELETE FROM tempExtract", , lngRecordsAffected)
Debug.Print lngRecordsAffected & " records deleted."
Call SQLRun("INSERT INTO tempExtract SELECT (" _
& DLookup("Value", "CONFIG_t", "Item = 'Xargs'" _
& ") FROM tempExtractID", , lngRecordsAffected)
Debug.Print lngRecordsAffected & " records inserted."
Суть в том, что если в операторе Execute есть ошибки, все это завершится ошибкой (и появится сообщение об ошибке - вы можете захотеть изменить его так, чтобы при возникновении ошибки он возвращал -1 или что-то подобное вместо этого) выскочить MsgBox).
Чаще всего я использую эту функцию, передавая предварительно кэшированную переменную базы данных, поэтому не хочу впоследствии ее очищать. Если вы используете другую базу данных, отличную от CurrentDB (), вы действительно хотите убедиться, что любая переменная базы данных, указывающая на вашу внешнюю базу данных, закрыта и имеет значение Nothing. Без этого сохраняются блокировки для объектов базы данных верхнего уровня, а файл LDB остается открытым и активным.