Pregunta

Perdón por el muro de mensajes de texto, pero este requiere expirar, demasiado código para publicar ...

Estoy importando archivos de ancho fijo en acceso en métodos que requieren la entrada de datos. Importo el archivo usando transferText en dos especificaciones (una global, la otra es una circunstancia especial).

Tengo una función que utiliza DAO para recorrer todos los objetos de campo en TableDefs para construir una tabla duplicada, incluida una PK de AutoIncrement, de modo que tengo la capacidad de editar estos registros. Empujo los datos en esa tabla con INSERT INTO.

Funciona muy bien. Se encuentran errores, el usuario entra en la entrada de datos para corregirlos manualmente, lo que supera la selección de 400 líneas de caracteres y reorganiza todo de la forma en que se supone que debe ser. ¡Funciona muy bien!

El problema : cuando se realizan los cambios en la entrada de datos, se presiona un botón de confirmación que llama a una función dentro de un módulo fuera del formulario. Cierra el formulario de entrada de datos y devuelve la información a la tabla original menos la PK autoincrementada, y se supone que DEBE GOTAR la tabla replicada con ID, y genera una nueva buscando nuevamente errores ...

Empuja al original muy bien, pero no BAJARÁ la tabla de ID. Siempre vuelve a mí con un mensaje que indica que esta tabla está bloqueada. He notado que la tabla está bloqueada indefinidamente hasta que todas las funciones / subs salen. En cualquier momento al recorrer el código, no puedo eliminarlo manualmente, una vez que la ejecución ha finalizado, puedo eliminarlo.

Supongo que, dado que llamé a través de un comando en el formulario, el bloqueo no se liberará hasta que finalice todo el código y se pueda llamar al formulario terminar y hacer lo suyo. ¿Alguna idea? Sí, esto es muy bárbaro, pero funciona bastante bien, solo necesito poder extraer esta otra tabla del planeta para poder volver a copiar una copia actualizada ...

En el peor de los casos, puedo hacer que el usuario cierre el formulario y presione otro botón en el formulario principal, pero esto está siendo diseñado en gran medida teniendo en cuenta la complicidad del usuario. Sin embargo, esto ahora tiene toda mi atención y me gustaría al menos encontrar una solución, incluso si no es la óptima.

-EDIT-

Se usan dos formas en este problema

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.

Formulario B - Formulario de entrada de datos

Tan pronto como abro este formulario, la tabla tempExtractID se bloquea y no puedo soltar la tabla. El origen de registros del formulario consulta tempExtractID contra los ID en Problems_t para devolver solo lo que necesitamos para ingresar.

No puedo descartar la tabla hasta que el formulario haya terminado por completo. Se presiona el botón en el formulario de Entrada de datos para confirmar los cambios, en los que solo hay 5 líneas de código que se activan antes de que aparezca mi error de bloqueo.

*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"

Este es el único código que se ejecuta entre el momento en que se abre el formulario (donde la tabla se bloquea por primera vez) y continúa bloqueado hasta que todos los subs & amp; funciones completadas.

¿Fue útil?

Solución

Sugiero configurar el origen de registros del formulario en vbNullString y luego eliminar la tabla. Esto debería funcionar, a menos que también tenga cuadros combinados y demás vinculados a esta tabla.

Otros consejos

Sin código es difícil de decir, pero si está utilizando DAO, necesita limpiar sus objetos de código. Eso significa establecer en Nothing los objetos de su base de datos y cerrar y establecer en Nothing cualquier objeto de conjunto de registros.

  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

Si bien se supone que VBA limpia estos objetos cuando están fuera de alcance, no es 100% confiable (porque VBA utiliza el recuento de referencias para realizar un seguimiento de si se puede liberar un objeto, y no siempre sabe cuándo las referencias han sido borradas).

Los objetos que quedan abiertos son la fuente más probable de los bloqueos, por lo que debe asegurarse de que está limpiando las variables de sus objetos una vez que haya terminado con ellos.

EDITAR después de ver que está usando DoCmd.RunSQL:

El uso de DoCmd.RunSQL es probablemente la causa del problema. Sin duda, es algo que le quita la gestión programática de sus conexiones. Si usa DAO en su lugar, tendrá control sobre la conexión, además de evitar el verdadero peligro de DoCmd.RunSQL, que es que no maneja los errores. Si una instrucción DML o DDL no puede completarse con éxito en su totalidad, todo debería fallar. Por ejemplo, si está agregando 100 registros y 10 de ellos fallan por violaciones clave, DoCmd.RunSQL agregará de forma transparente los 90 y NO REPORTARÁ LAS 10 FALLAS. Es lo mismo con las actualizaciones y cualquier otra declaración DML / DDL. DoCmd.RunSQL " útilmente " completa silenciosamente tantas actualizaciones como sea posible, dejándolo sin tener idea de que algunas de ellas no pudieron completarse.

De acuerdo, en algunos casos es posible que desee que eso suceda, por ejemplo, si está agregando registros que sabe que pueden tener colisiones PK y no desea gastar los ciclos de la CPU en una unión externa que elimine los duplicados del conjunto de registros que está agregando.

Pero la mayoría de las veces, ese no es el caso.

Como dije en mi comentario anterior, utilizo una función que está diseñada para reemplazar de manera transparente DoCmd.RunSQL y utiliza una instrucción DAO Execute y manejo de errores. Lo publiqué un par de veces en SO ( aquí hay uno ), y aquí está la versión que tengo en producción para mi proyecto de desarrollo más activo actualmente:

  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

(las transacciones se comentan porque estaban causando problemas que no tuve tiempo de solucionar)

Puede reemplazar estas líneas suyas:

  DoCmd.RunSQL "DELETE FROM tempExtract"
  DoCmd.RunSQL "INSERT INTO tempExtract SELECT (" _
    & DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"

... con esto:

  SQLRun "DELETE FROM tempExtract"
  SQLRun "INSERT INTO tempExtract SELECT (" _
    & DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"

También puedes hacer esto:

  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."

Dado que la función devuelve .RecordsAffected para cada Ejecutar, puede imprimir en la Ventana Inmediata, o puede asignar el valor de retorno a una variable, o pasarle una variable existente y trabajar con esa variable así:

  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."

El punto es que si hay errores en la instrucción Ejecutar, todo fallará (y aparecerá un mensaje de error; es posible que desee cambiarlo para que, si hay un error, devuelva -1 o algo similar) de hacer estallar un MsgBox).

Utilizo esta función con mayor frecuencia al pasar una variable de base de datos previamente almacenada en caché, por lo que no quiero limpiarla después. Si está utilizando una base de datos diferente a CurrentDB (), realmente desea asegurarse de que cualquier variable de base de datos que apunte a su base de datos externa esté cerrada y establecida en Nothing. Sin eso, los bloqueos se mantienen en los objetos de la base de datos de nivel superior y el archivo LDB permanece abierto y activo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top