Форма надежно заблокировала мою таблицу даже после docmd.close

StackOverflow https://stackoverflow.com/questions/1647591

Вопрос

Извините за стену текста, ребята, но это требует объяснений, слишком много кода для публикации...

Я импортирую файлы фиксированной ширины в access методами, требующими ввода данных.Я импортирую файл, используя TransferText, в две спецификации (одна глобальная, другая - особые обстоятельства).

У меня есть функция, которая использует DAO для циклического перебора всех объектов Field в TableDefs для создания дублирующейся таблицы, включая автоинкремент PK, поэтому у меня есть возможность редактировать эти записи.Я помещаю данные в эту таблицу с помощью INSERT INTO.

Отлично работает.Обнаруживаются ошибки, пользователь переходит к вводу данных, чтобы вручную исправить их, что лучше, чем перебирать 400 символьных строк и реорганизовывать все так, как должно быть.Отлично работает!

В чем проблема:При внесении изменений в ввод данных нажимается кнопка фиксации, которая вызывает функцию внутри модуля вне формы.Он закрывает форму ввода данных и возвращает информацию обратно в исходную таблицу за вычетом автоматически созданного PK, и ПРЕДПОЛАГАЕТСЯ, что реплицированная таблица с идентификаторами удалится и будет сгенерирована новая, которая снова будет искать ошибки...

Он просто отлично возвращается к оригиналу, но не удаляет таблицу идентификаторов.Всегда возвращается ко мне с сообщением, указывающим, что эта таблица заблокирована.Я заметил, что таблица заблокирована на неопределенный срок до тех пор, пока все функции / подменю не завершатся.При любом просмотре кода я не могу удалить его вручную, как только выполнение завершится, я смогу удалить его.

Я предполагаю, что, поскольку я вызвал это через команду в форме, блокировка не будет снята до тех пор, пока весь код не завершится и форма terminate не сможет быть вызвана и сделать свое дело.Есть какие-нибудь мысли?Да, это очень варварски, но работает довольно хорошо, мне просто нужно иметь возможность удалить эту другую таблицу с планеты, чтобы я мог удалить обновленную копию...

В худшем случае я могу заставить пользователя закрыть форму и нажать другую кнопку в основной форме, но это в значительной степени разрабатывается с учетом компетентности пользователя.Однако сейчас это привлекает все мое внимание, и я хотел бы, по крайней мере, найти решение, даже если оно не является оптимальным.

-РЕДАКТИРОВАТЬ-

В этой задаче используются две формы

FormA (Role: Load in and search for problems)

Examine button is pressed that:

 - Uses TextTransfer based on predefined specs into tempExtract to
       import the file

 - DAO fires off on the Fields collection in tableDefs for
   tempExtract, creates new table tempExtractID

 - Performs searches through the file to find errors.  Errors are saved to
   a table Problem_t.  Table contains Problem_ID (Set from the ID field
   added to tempExtractID) and Description

 - Execution of these tasks is successfully requerying the initial
   form to showing a list of problems and number of occurances.  A button
   gains visibility, with onClick that opens the form DataEntry.            

 - At this point in the code after DAO execution, I can DROP the table
   tempExtractID.  DAO is NOT used again and was only used to build a new table.

FormB - Форма для ввода данных

Как только я открываю эту форму, таблица tempExtractID становится заблокированной, и я не могу удалить таблицу.Источник записи в форме запрашивает tempExtractID у идентификатора в Problems_t, чтобы вернуть только то, что нам нужно ввести.

Я не могу удалить таблицу до тех пор, пока форма не будет полностью закрыта.Кнопка в форме ввода данных нажимается для фиксации изменений, в которых присутствуют только 5 строки кода, которые должны сработать до того, как я получу сообщение об ошибке блокировки.

*Xargs refers to the list of Field names pulled earlier through DAO.  As DAO loops through Field objects, the physical names are added to an Xargs String which is placed in this table.  Basically everything but the AutoNumber is being inserted back

    docmd.Close acForm, "frmDataEntry", acSaveNo
    call reInitializeExtract
         > docmd.RunSQL "DELETE FROM tempExtract"
         > docmd.RunSQL "INSERT INTO tempExtract SELECT (" & DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"
    docmd.DeleteObject acTable, "tempExtractID"

Это единственный код, который выполняется с момента открытия формы (когда таблица впервые блокируется) и продолжает блокироваться до тех пор, пока не будут завершены все вспомогательные операции и функции.

Это было полезно?

Решение

Я предлагаю установить для recordsource формы значение vbNullString, а затем удалить таблицу.Это должно сработать, если только у вас также нет списков со списком и так далее, привязанных к этой таблице.

Другие советы

Без кода трудно сказать, но если вы используете DAO, вам нужно очистить объекты вашего кода.Это означает, что ваши объекты базы данных будут сведены к нулю, а также закрытие и сведение к нулю любых объектов набора записей.

  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 СБОЯХ.То же самое происходит с updates и любым другим оператором DML / DDL.DoCmd.RunSQL "услужливо" автоматически завершает столько обновлений, сколько может, оставляя вас без понятия о том, что некоторые из них не удалось завершить.

Конечно, в некоторых случаях вы могли бы захотеть, чтобы это произошло, например, если вы добавляете записи, которые, как вы знаете, могут иметь PK-коллизии, и не хотите тратить циклы процессора на внешнее объединение, которое устраняет дубликаты из набора добавляемых вами записей.

Но в большинстве случаев это не так.

Как я уже говорил в своем комментарии выше, я использую функцию, которая предназначена для прозрачной замены DoCmd.RunSQL и использует инструкцию выполнения DAO и обработку ошибок.Я публиковал это пару раз на 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."

Поскольку функция возвращает .RecordsAffected для каждого выполнения, вы можете печатать в немедленном окне, или вы могли бы присвоить возвращаемое значение переменной, или передать через нее существующую переменную и работать с этой переменной таким образом:

  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 остается открытым и активным.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top