none
Aktualisierung in verknüpfter Tabelle schlägt fehl - Access 2010, SQL Server, ODBC

    Frage

  • Hallo zusammen,

    in einer Access-Anwendung, die auf per ODBC eingebundene SQL Server-Tabellen und -Views zugreift, habe ich unter bestimmten Konstellationen Probleme mit der Datensatzaktualisierung. Es gibt eine Reihe Listenformularen, deren Daten per Abfrage generiert werden und innerhalb der Listen nicht aktualisiert werden sollen. Per Doppelklick auf eine ID wird der eigentliche Datensatz geöffnet. Das Problem ist, dass ich den Datensatz nicht aktualisieren kann, wenn eine der Listen geöffnet ist, weil der Vorgang dann in einen Timeout läuft. Ich habe das nun so geregelt, dass vor dem Laden des Formulars die Listenformulare geschlossen werden. Danach kann ich den Datensatz des Pflegeformulars auch problemlos aktualisieren.

    Es ist übrigens unerheblich, welche Technik ich anwende, um den Datensatz zu aktualisieren. Aus dem Pflegeformular heraus wird der Datensatz per Stored Procedure aktualisiert. Bei geöffneter Liste funktioniert das so wenig wie die Verwendung von Aktualisierungsabfragen, Updates auf Recordets oder Pass-Through-Abfragen. Beim Öffnen des Pflegeformulars wird eine Abfrage geöffnet, deren Daten in ein Objekt geschrieben werden. Die Abfrage gibt daher auch nur ein ReadOnly-Recordset zurück, das unmittelbar darauf verworfen wird. Alle Datenänderungen werden zunächst am Objekt vorgenommen und dann per SP in die Datenbank zurückgeschrieben.

    Beim Aufruf eines Datensatzes muss der aktuelle Benutzer in ein Feld der Tabelle geschrieben werden. Steht in dem Feld etwas drin, kann der Datensatz von keinem anderen Benutzer geöffnet werden und es wird eine Meldung ausgegeben, welcher Benutzer den Datensatz in Bearbeitung hat. Diese Aktualisierungsabfrage wird beim Aktivieren des Formulars ausgeführt, nachdem die aufrufende Liste bereits geschlossen ist. Sie läuft trotzdem in einen Timeout. Beim Schließen des Pflegeformulars wird ebenfalls eine Aktualisierungsabfrage gestartet, die das entsprechende Feld wieder löscht. Das funktioniert einwandfrei.

    Dieses merkwürdige Verhalten kostet mich schon immens viel Zeit. Früher habe ich Access-Frontends als adp/ade entwickelt, wobei solche Szenarien absolut problemlos zu entwickeln waren. Habe ich einen Denkfehler im Zusammenhang mit den ODBC-verknüpften Tabellen?

    Für jedwede Hilfe vielen Dank.

    M. Schörner


    Montag, 23. Oktober 2017 16:12

Antworten

  • Das Problem konnte ich folgendermaßen lösen:

    Private Sub R_ID_DblClick(Cancel As Integer)
        retoureID = Me.R_ID
        AufrufendesFormular = "frmOffenePostenAlleMitarbeiter"
        Me.Recordset.Close
        Set Me.Recordset = Nothing
        DoCmd.Close
        Call prcOpenSpecialOP(retoureID)
    End Sub

    Dadurch ist sichergestellt, dass das Recordset samt Zugriffshandle auf die SQl Server Tabelle beendet ist. Der darauf folgende Aufruf der Aktualisierungsabfrage funktioniert danach perfekt.

    Jedem, der sich hier darüber Gedanken gemacht hat, danke ich.

    Liebe Grüße,
    Michael

    • Als Antwort markiert M. Schörner Dienstag, 24. Oktober 2017 13:12
    Dienstag, 24. Oktober 2017 13:10

Alle Antworten

  • Am 23.10.2017 schrieb M. Schörner:

    in einer Access-Anwendung, die auf per ODBC eingebundene SQL Server-Tabellen und -Views zugreift, habe ich unter bestimmten Konstellationen Probleme mit der Datensatzaktualisierung. Es gibt eine Reihe Listenformularen, deren Daten per Abfrage generiert werden und innerhalb der Listen nicht aktualisiert werden sollen. Per Doppelklick auf eine ID wird der eigentliche Datensatz geöffnet. Das Problem ist, dass ich den Datensatz nicht aktualisieren kann, wenn eine der Listen geöffnet ist, weil der Vorgang dann in einen Timeout läuft. Ich habe das nun so geregelt, dass vor dem Laden des Formulars die Listenformulare geschlossen werden. Danach kann ich den Datensatz des Pflegeformulars auch problemlos aktualisieren.

    Haben die Tabellen denn auch ein Timestamp Feld? Falls ja, hast Du das
    in Access eingebunden? Welche exakte Fehlermeldung kommt?

    Es ist übrigens unerheblich, welche Technik ich anwende, um den Datensatz zu aktualisieren. Aus dem Pflegeformular heraus wird der Datensatz per Stored Procedure aktualisiert. Bei geöffneter Liste funktioniert das so wenig wie die Verwendung von AKtualisierungsabfragen, Updates auf Recordets oder Pass-Through-Abfragen. Beim Öffnen des Pflegeformulars wird eine Abfrage geöffnet, deren Daten in ein Objekt geschrieben werden. Die Abfrage gibt daher auch nur ein ReadOnly-Recordset zurück, das unmittelbar darauf verworfen wird. Alle Datenänderungen werden zunächst am Objekt vorgenommen und dann per SP in die Datenbank zurückgeschrieben.

    Ohne eingebundene Tabellen zu arbeiten, wäre an dieser Stelle
    vermutlich zielführender.
    Bern Jungbluth hat dazu auf einer der letzten AEKs einen Vortrag
    gehalten. Im Downloadbereich gibt es auch eine kleine Beispiel
    Datenbank.

    Servus
    Winfried


    Access-FAQ: http://www.donkarl.com/AccessFAQ.htm Access-Stammtisch: http://www.access-muenchen.de
    NNTP-Bridge für MS-Foren: http://communitybridge.codeplex.com/
    vbeTwister: http://www.vbetwister.com/

    Dienstag, 24. Oktober 2017 05:09
  • Hallo Winfried,

    dank für Deine Antwort.

    Die Fehlermeldung ist

    "Error 3157 (ODBC: Aktualisierung in einer verknüpften Tabelle 'dbo_tblRetouren' fehlgeschlagen.) in procedure LockUnlockRetoure of Klassenmodul clsRetoureDB"

    Ein Timestamp-Feld ist in der zugrunde liegenden Retouren-Tabelle vorhanden. Das Feld ist in alle Abfragen etc. eingebunden. Die Aufrufreihenfolge ist folgende:

    1. Öffnen der Liste (die eigentliche Liste ist ein Unterformular des aufgerufenen Formulars).
    2. Doppelklick auf die ID des zu öffnenden Datensatzes ruft eine Sub auf, die überprüft, ob ein anderer Benutzer den Datensatz gerade bearbeitet, alle offenen Listenformulare schließt und dann das Pflegeformular öffnet.
    3. Im Form_Load-Ereignis des Pflegeformulars wird das dem Formular zugrunde liegende Objekt erzeugt und mit Daten gefüllt, die eine Access-Abfrage liefert.
    4. Im Form_Activate-Ereignis soll in die Tabelle der aktuelle Benutzer geschrieben werden; hier läuft die Aktion in den Timeout (es ist vollkommen gelichgültig, wo und wann ich diese Aktionsabfrage aufrufe; ich habe weitgehend alle sinnvollen Positionen ausprobiert).
    5. Im Form_Close-Ereignis wird die Sub erneut aufgerufen, um das Benutzerfeld in der Tabelle wieder zu leeren. Hier läuft die Aktionsabfrage nie in einen Timeout.
    6. Das Listenformular wird wieder angezeigt.

    Selbst wenn ich den Aufruf des Pflegeformulars außen vor lasse, d.h. die Aktualisierung der Tabelle direkt nach dem Schließen der Liste vornehme, laufe ich in den Timeout. Kann es sein, dass zwar das Formular geschlossen ist bzw. nicht mehr angezeigt wird, das zugrunde liegende Recordset aber so lange existent bleibt, bis alle Funktionsaufrufe abgeschlossen sind?

    Der Code sieht folgendermaßen aus (aufs Wesentliche gekürzt):

    Private Sub R_ID_DblClick(Cancel As Integer)
        retoureID = Me.R_ID
        AufrufendesFormular = "frmOffenePostenAlleMitarbeiter"
        DoCmd.Close acForm, AufrufendesFormular
        Call prcOpenSpecialOP(retoureID)
    End Sub
    
    Sub prcOpenSpecialOP(ByVal pLfdNr As Long)
        If IsFormLoaded("frmMitarbeiterRetouren") = True Then
            DoCmd.Close acForm, "frmMitarbeiterRetouren"
        End If
        ...
        myRetMgr.RetoureLockUnlock pLfdNr, CurrentUser.Mitarbeiter    
    End Sub
    
    Public Sub LockUnlockRetoure(ByVal Retoure As Long, ByVal CurrentUser As String)
        Dim db As Database
        Dim qdef As QueryDef
        Set db = CurrentDb    
        If Len(CurrentUser) > 0 Then
            Set qdef = db.QueryDefs("qupdRetoureLock")
            qdef.Parameters("@retoure").Value = Retoure
            qdef.Parameters("@currentUser").Value = CurrentUser
            qdef.Execute dbSeeChanges
        Else
            Set qdef = db.QueryDefs("qupdRetoureUnlock")
            qdef.Parameters("@retoure").Value = Retoure
            qdef.Execute dbSeeChanges
        End If    
        Set qdef = Nothing
        Set db = Nothing
    End Sub

    Viele Grüße,
    Michael

    Dienstag, 24. Oktober 2017 09:04
  • Das Problem konnte ich folgendermaßen lösen:

    Private Sub R_ID_DblClick(Cancel As Integer)
        retoureID = Me.R_ID
        AufrufendesFormular = "frmOffenePostenAlleMitarbeiter"
        Me.Recordset.Close
        Set Me.Recordset = Nothing
        DoCmd.Close
        Call prcOpenSpecialOP(retoureID)
    End Sub

    Dadurch ist sichergestellt, dass das Recordset samt Zugriffshandle auf die SQl Server Tabelle beendet ist. Der darauf folgende Aufruf der Aktualisierungsabfrage funktioniert danach perfekt.

    Jedem, der sich hier darüber Gedanken gemacht hat, danke ich.

    Liebe Grüße,
    Michael

    • Als Antwort markiert M. Schörner Dienstag, 24. Oktober 2017 13:12
    Dienstag, 24. Oktober 2017 13:10
  • Hallo,
     
    M. Schörner wrote:
     
    > in einer Access-Anwendung, die auf per ODBC eingebundene SQL
    > Server-Tabellen und -Views zugreift, habe ich unter bestimmten
    > Konstellationen Probleme mit der Datensatzaktualisierung. Es gibt eine
    > Reihe Listenformularen, deren Daten per Abfrage generiert werden und
    > innerhalb der Listen nicht aktualisiert werden sollen. Per Doppelklick
    > auf eine ID wird der eigentliche Datensatz geöffnet. Das Problem ist,
    > dass ich den Datensatz nicht aktualisieren kann, wenn eine der Listen
    > geöffnet ist, weil der Vorgang dann in einen Timeout läuft. Ich habe das
    > nun so geregelt, dass vor dem Laden des Formulars die Listenformulare
    > geschlossen werden. Danach kann ich den Datensatz des Pflegeformulars
    > auch problemlos aktualisieren.
     
    Liegt vermutlich daran, dass die Listen relativ lang sind und vom Server
    nicht in einem Zug geladen werden. Anders als Access als Backend erwartet
    der Server, dass die angeforderten Daten vom Client auch wirklich abgeholt
    werden, und nicht, wie in Access praktiziert, im Hintergrund nachgeladen
    werden, während der Benutzer schon mit dem arbeiten kann, was er sieht. Im
    Activity Monitor auf dem Server sieht man dann das berühmte
    Async_Network_IO und nachfolgende Prozesse müssen warten -> Timeout.
     
    Umgehen lässt sich das Problem, indem man nach dem Öffnen direkt einen
    MoveLast macht, was Access zwingt, das angeforderte Recordset komplett zu
    laden, damit der Server wieder frei für andere Anfragen ist.
     
    Also, im gebundenen Formular:
     
    Private Sub Form_Open(Cancel As Integer)
      Me.Painting = False
      With Me.Recordset
        .MoveLast
        .MoveFirst
      End With
      Me.Painting = True
    End Sub
     
    Sollten im geöffneten Formular Requeries gemacht werden, gilt das gleiche.
    Wenn ein bestimmter Datensatz angesprungen werden soll, siehe
     
    > Es ist übrigens unerheblich, welche Technik ich anwende, um den
    > Datensatz zu aktualisieren. Aus dem Pflegeformular heraus wird der
    > Datensatz per Stored Procedure aktualisiert.
     
    Es ist meistens problematisch, mit unterschiedlichen Methoden (gebundenes
    Formular, SQL, PT) auf dieselben Daten zugreifen zu wollen. Keine der
    Methoden ist besser oder schlechter, je nach Anwendung sind manchmal
    gebundene Formulare besser (Komfort beim Datenzugriff), manchmal SQL bzw.
    PassThrough (Performance).
     
    Der Rest der Probleme sieht nach Folgefehlern aus.
     
    Gruss - Peter
     
    --
     
    Samstag, 18. November 2017 09:34
    Moderator