Question

Désolé pour le mur de texte des gars, mais celui-ci nécessite une explication, beaucoup trop de code à publier ...

J'importe des fichiers de largeur fixe dans l'accès à des méthodes nécessitant une entrée de données. J'importe le fichier utilisant transferText dans deux spécifications (l'une globale, l'autre étant une circonstance spéciale).

J'ai une fonction qui utilise DAO pour parcourir tous les objets Field de TableDefs afin de créer une table en double, y compris une clé à incrémentation automatique, afin de pouvoir modifier ces enregistrements. J'insère les données dans cette table avec INSERT INTO.

Fonctionne très bien. Des erreurs sont trouvées, l'utilisateur entre dans la saisie des données pour les corriger manuellement, ce qui bat le crible sur 400 lignes de caractères et réorganise tout ce qu'il est censé être. Fonctionne très bien!

Le problème : lorsque vous modifiez la saisie des données, vous appuyez sur un bouton de validation qui appelle une fonction dans un module en dehors du formulaire. Il ferme le formulaire de saisie de données et renvoie les informations à la table d'origine moins la PK auto-incrémentée. Il est SUPPOSÉ pour supprimer la table répliquée avec les ID et en générer un nouveau en recherchant à nouveau les erreurs ...

Cela rétablit parfaitement l’original, mais cela ne supprime pas la table des identifiants. Me renvoie toujours avec un message indiquant que cette table est verrouillée. J'ai remarqué que la table est indéfiniment verrouillée jusqu'à ce que toutes les fonctions / sous-sorties soient fermés. À tout moment, je ne peux pas le supprimer manuellement, une fois l'exécution terminée, je suis en mesure de le supprimer.

Je suppose que puisque j'ai appelé cela via une commande dans le formulaire, le verrou ne sera pas relâché tant que tout le code ne sera pas terminé et que le formulaire se terminant ne pourra être appelé et fera son travail. Des pensées? Oui, c’est très barbare, mais cela fonctionne assez bien, je dois juste pouvoir déchirer cette autre table de la planète pour pouvoir en refaire une copie mise à jour ...

Dans le pire des cas, je peux demander à l'utilisateur de fermer le formulaire et d'appuyer sur un autre bouton du formulaire principal, mais ce dernier est conçu de manière à tenir compte de la compétence de l'utilisateur. Cependant, cela retient maintenant toute mon attention et je voudrais au moins trouver une solution, même si ce n’est pas la solution optimale.

-EDIT -

Deux formulaires sont utilisés dans ce problème

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 - Formulaire de saisie de données

Dès que j'ouvre ce formulaire, la table tempExtractID est verrouillée et je ne peux pas supprimer la table. Le recordsource du formulaire interroge tempExtractID par rapport à l'ID dans Problems_t pour ne renvoyer que ce dont nous avons besoin.

Je ne peux pas supprimer la table tant que le formulaire n'est pas complètement terminé. Vous devez appuyer sur le bouton du formulaire de saisie de données pour valider les modifications. Seules 5 lignes de code sont activées avant que je ne reçois mon erreur de verrouillage.

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

C’est le seul code exécuté entre le moment où le formulaire est ouvert (où la table est verrouillée pour la première fois) et continue de l’être jusqu'à ce que tous les sous-éléments & amp; les fonctions sont terminées.

Était-ce utile?

La solution

Je suggère de définir la source d’enregistrement du formulaire sur vbNullString, puis de supprimer la table. Cela devrait fonctionner, sauf si vous avez également des listes déroulantes, etc., liées à ce tableau.

Autres conseils

Sans code, c'est difficile à dire, mais si vous utilisez DAO, vous devez nettoyer vos objets de code. Cela signifie que vous définissez les objets de la base de données sur Nothing, la fermeture et les objets du jeu d’enregistrements à 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

Bien que VBA soit censé nettoyer ces objets lorsqu'ils sortent de leur champ d'application, ce n'est pas fiable à 100% (car VBA utilise le comptage de références pour savoir si un objet peut être libéré, et il ne sait pas toujours quand les références ont été effacées).

Les objets laissés ouverts sont la source la plus probable des verrous. Vous devez donc vous assurer de nettoyer vos variables d'objet une fois que vous en avez terminé.

EDIT après avoir constaté que vous utilisez DoCmd.RunSQL:

L'utilisation de DoCmd.RunSQL est probablement la cause du problème. C'est certainement quelque chose qui enlève votre gestion programmatique de vos connexions. Si vous utilisez plutôt DAO, vous aurez le contrôle de la connexion, tout en évitant le véritable piège de DoCmd.RunSQL, à savoir qu'il ne gère pas les erreurs. Si une instruction DML ou DDL ne peut pas être complétée avec succès, le tout doit échouer. Par exemple, si vous ajoutez 100 enregistrements et que 10 d’entre eux échouent pour des violations de clé, DoCmd.RunSQL ajoutera de manière transparente le 90 et NE SIGNALERA PAS LES 10 DÉFAILLANCES. C'est la même chose avec les mises à jour et toutes les autres instructions DML / DDL. DoCmd.RunSQL " utilement " termine silencieusement autant de mises à jour que possible, sans que vous sachiez que certaines d'entre elles ont échoué.

Bien sûr, dans certains cas, vous souhaiterez peut-être que cela se produise, par exemple, si vous ajoutez des enregistrements susceptibles de provoquer des collisions PK et que vous ne souhaitez pas utiliser les cycles du processeur pour une jointure externe qui élimine les doublons de la mémoire. ensemble d'enregistrements que vous ajoutez.

Mais la plupart du temps, ce n'est pas le cas.

Comme je l'ai dit dans mon commentaire ci-dessus, j'utilise une fonction conçue pour remplacer de manière transparente DoCmd.RunSQL et utilise une instruction DAO Execute et une gestion des erreurs. Je l'ai posté à quelques reprises sur SO ( en voici un ), et voici la version que j'ai en production utilisée dans mon projet de développement le plus actif:

  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

(les transactions sont commentées car elles causaient des problèmes que je n'avais pas le temps de résoudre)

Vous pouvez remplacer ces lignes:

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

... avec ceci:

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

Vous pouvez également faire ceci:

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

Etant donné que la fonction renvoie le .RecordsAffected pour chaque exécution, vous pouvez imprimer dans la fenêtre Immédiate, ou vous pouvez affecter la valeur de retour à une variable, ou lui transmettre une variable existante et utiliser cette variable comme suit:

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

Le fait est que s'il y a des erreurs dans l'instruction Execute, le tout échouera (et affichera un message d'erreur - vous voudrez peut-être le modifier de sorte que s'il y a une erreur, elle retourne -1 ou un message similaire à la place. faire apparaître une MsgBox).

J'utilise cette fonction le plus souvent en transmettant une variable de base de données pré-mise en cache, je ne souhaite donc pas la nettoyer par la suite. Si vous utilisez une base de données différente de CurrentDB (), vous voulez vraiment vous assurer que toute variable de base de données pointant sur votre base de données externe est fermée et définie sur Nothing. Sans cela, les verrous sont conservés sur les objets de base de données de niveau supérieur et le fichier LDB reste ouvert et actif.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top