Форма закрыла мою таблицу, даже после того, как docmd.close - PullRequest
3 голосов
/ 30 октября 2009

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

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

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

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

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

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

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

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

-EDIT-

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

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.

Форма B - Форма ввода данных

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

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

Ответы [ 2 ]

6 голосов
/ 30 октября 2009

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

4 голосов
/ 30 октября 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."

Поскольку функция возвращает .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 остается открытым и активным.

...