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

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

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

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

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

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

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

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

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

-ИЗМЕНИТЬ-

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

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"

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


person Mohgeroth    schedule 30.10.2009    source источник
comment
Q1: Эта проблема постоянна или прерывиста? Периодически может быть задержка в файле ldb. Q2: Почему не обрабатывается транзакция expression.RunSQL(SQLStatement, UseTransaction)? возможно, это могло бы облегчить откат. Также учитывайте ограничения docmd, если вы используете Access97 или более раннюю версию.   -  person heferav    schedule 30.10.2009
comment
Проблема постоянна, и из-за характера этих файлов нет комбинации, которую можно было бы установить в качестве первичного ключа, я был вынужден переместить эти записи, потому что форма не позволяла мне изменять какие-либо данные, поскольку уникальный идентификатор не был найден. Access 2000, и я использовал таблицу сброса docmd.runsql и DBEngine, чтобы попытаться стереть таблицы, а также вручную перейти к самой таблице и попытаться удалить ее, пока форма открыта и код проходит через нее, жестче, чем форт нокс :(   -  person Mohgeroth    schedule 30.10.2009
comment
Прекратите использовать DoCmd.RunSQL — он дает ненадежные результаты, поскольку игнорирует ошибки. Простая в использовании альтернатива — моя функция SQLRun(): stackoverflow.com/questions/343396/ -- если вам нравится функция. пожалуйста, проголосуйте за этот ответ, так как в исходной версии была ошибка, из-за которой он был отклонен, а затем проголосовавшие против не вернулись и не ответили на исправление.   -  person David-W-Fenton    schedule 31.10.2009


Ответы (2)


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

person Fionnuala    schedule 30.10.2009
comment
Я сделаю это сегодня, когда зайду - person Mohgeroth; 30.10.2009
comment
Решено несколько недель назад, просто забыл опубликовать принятый ответ, это помогло, спасибо! - person Mohgeroth; 25.11.2009

Без кода трудно сказать, но если вы используете 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."

Поскольку функция возвращает .RecordsAffected для каждого выполнения, вы можете печатать в 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 или что-то в этом роде вместо того, чтобы выталкивать сообщение об ошибке). Сообщения).

Я использую эту функцию чаще всего, передавая предварительно кэшированную переменную базы данных, поэтому я не хочу потом ее очищать. Если вы используете другую базу данных, отличную от CurrentDB(), вы действительно хотите убедиться, что любая переменная базы данных, указывающая на ваш внешний db, закрыта и установлена ​​​​в Nothing. Без этого на объектах базы данных верхнего уровня сохраняются блокировки, а файл LDB остается открытым и активным.

person David-W-Fenton    schedule 30.10.2009