#Gelöscht in Datenfeldern

28. Dezember 2015

Als Datenbankentwickler mit Access ist man immer wieder mit der Situation konfrontiert, dass Access in den Datenzeilen statt der erwarteten Tabelleninhalte #Gelöscht anzeigt oder dass nach dem Speichern von neuen Datensätzen falsche Daten angezeigt werden. Das sieht dann zum Beispiel so aus (Bild zeigt die englische Variante):

Statt der Daten wird #Gelöscht angezeigt

In den meisten Fällen, in denen Access statt der gewünschten Daten ein #Gelöscht anzeigt, gibt es eine einfache Ursache: Access speichert das Ergebnis einer Datenbankabfrage meist in sogenannten Dynasets. Das bedeutet, dass nicht die kompletten Datenbankinhalte des entsprechenden Recordsets sondern nur die entsprechenden PrimaryKeys gespeichert werden, was natürlich viel weniger Speicherplatz und Zeit bei der Datengenerierung benötigt. Erst wenn man sich die entsprechende Daten zum Beispiel durch Herunterscrollen wirklich anschauen möchte, werden die zu einem PrimaryKey gehörenden Daten aus der Datenbank ausgelesen.

Das funktioniert jedoch nur dann gut, wenn für alle Tabellen "vernünftige" PrimaryKeys definiert sind. Vernünftig bedeutet in dem Zusammenhang, dass für jede Tabelle eine Autowert-Spalte als Zähler mit der PrimaryKey-Eigenschaft eingerichtet wird. Alle anderen PrimaryKeys wie zum Beispiel Fließkommazahlen oder Strings sind eigentlich nicht geeignet. Weitere eindeutige Schlüssel, sogenannte CandidateKeys, können unabhängig davon noch zusätzlich definiert werden. Der für die Autowert-Spalte benötigte zusätzliche Speicherplatz spielt auf heutigen Computersystemen denke ich keine Rolle mehr.

Mit der oben beschriebenen Regel sind die allermeisten Probleme in Bezug auf die #Gelöscht-Datensätze behoben. Es gibt jedoch insbesondere im Zusammenspiel mit SQL-Servern ganz hartnäckige Fälle, in denen nach dem Speichern eines Datensatzes entweder #Gelöscht oder auch ein komplett anderer Datensatz angezeigt wird. Dies zeigt der untenstehende Screenshot:

Ein neu angelegter Datensatz wird als gelöscht angezeigt

Ich habe schon Stunden erfolglos damit zugebracht, eine Systematik und daraus resultierend eine Umgehung für dieses Problem zu finden. Am Ende habe ich akzeptiert, dass beim Anlegen eines neuen Datensatzes die korrekte ID des neuen Datensatzes aus irgendeinem Grund nicht den Weg zurück zu Access-Applikation findet, sondern Access von einem falschen Wert ausgeht. Da diese Beobachtung eigentlich nur bei Tabellen mit verhältnismäßig vielen Datensätzen gemacht wurde, vermute ich irgendeine Form von Überlauf in der Datenbankschnittstelle.

Ich habe in meiner Not dann etwas eigentlich Unschönes gemacht, das in meinen Applikationen das Problem aber zum Verschwinden gebracht hat: Vor Aktualisierung des Datensatzes bestimme ich von der Applikation aus den nächsten freien Autowert. Dies ist eigentlich die primäre Aufgabe der Datenbank, führt aber, wie oben beschreiben, zu einem Fehler. Da es sein kann, dass genau in demselben Augenblick ein anderer Client ebenfalls den nächsten freien Autowert bestimmen will, kopple ich diesen Vorgang noch mit einer Sperrfunktion, so dass immer nur ein Client zur Zeit einen neuen Autowert bestimmen kann. Ist die Funktion gesperrt, warte ich einen Moment und probiere es dann noch einmal. Da die Anzahl der Benutzer in den Applikationen, in denen dieser Workaround zum Einsatz kommt, nicht zu groß ist, hat es hier noch nie Probleme gegeben. Das ist zwar keine wirklich schöne Programmierung, aber manchmal heiligt der Zweck halt die Mittel.

Jetzt aber noch einmal im Detail: Im Event-Handler des Ereignisses Vor Aktualisiertung weise ich dem Feld mit der ID den selbst ermittelten ID-Wert zu. Etwa so:

FTei!AufTei_ID = AutoWert("Auftraege_Teile", "AufTei_ID")

Die Funktion Autowert selbst sieht so aus:

Function AutoWert(Tabelle As String, Autowertspalte As String, Optional Sperr As Boolean = True) As Long
'
' Nach dem Speichern eines Datensatzes wird dieser noch einmal neu geladen. Manchmal passieren dabei Fehler,
' was sich in #gelöscht-Einträgen oder einfach in einem falsch angezeigten Datensatz mainfestiert. Darum wird der
' Wert des Zählerfeldes vor dem Speichern bestimmt.
'
Dim SName As String ' wird für die Funktion Sperren benötigt
Dim MaxID As Variant
Dim I As Integer
  
  GlobVarSetzen
   
  ' Sperrung für das Erzeugen eines Autowertes erzeugen
  If Sperr Then
    For I = 1 To 10
      If (Sperren("Autowert " & Tabelle, SName)) Then
        Exit For
      End If
     
      ' 2 Sekunden warten
      sSleep 2000
      
      If I = 10 Then
        Err.Raise 12345, "AutoWert()", "Es kann kein neuer Autowert für Tabelle " & Tabelle & " erzeugt werden."
        Exit Function
      End If
    Next I
  End If
  
  MaxID = DMax(Autowertspalte, Tabelle)
  
  If IsNull(MaxID) Then
    AutoWert = 1
  Else
    AutoWert = MaxID + 1
  End If
  
  EntSperren "Autowert " & Tabelle
  
End Function

Kommentare

  • Mathias Wieland
    am 04.11.2016
    Könnte es sein, dass in der entsprechenden per ODBC-verknüpften SQL Server-Tabelle keine TimeStamp-Spalte eingefügt ist. Access benötigt die Timestamp-Spalte, um Änderungen z.B. an Memo-Feldern nachzuvollziehen. Ich hatte im Mehrbenutzerbetrieb durch fehlende Memo-Felder auch schon Schreibkonflikte, die sich durch das Einfügen selbiger "verflüchtigt" haben. Hier ein weiterführender Link: http://stackoverflow.com/questions/3248967/write-conflict-messages-suddenly-start-happening-in-odbc-linked-tables/3302588#3302588

    Viele Grüße, Mathias Wieland
  • Anna
    am 04.11.2016
    Hallo Mathias,
    danke für den Kommentar. In unseren SQL-Datenbanken haben alle Tabellen grundsätzlich ein Timestamp-Feld - das kann also hier nicht das Problem sein. Es ist aber auf jeden Fall gut, noch mal darauf hinzuweisen, dass ein Timestamp unerlässlich ist.
    Schöne Grüße, Anna
  • Gerhard
    am 26.01.2017
    Hallo,

    ich habe diesen Fehler auch in der Konstellation MS Access/SQL Server gehabt und Stunden mit Recherche und Fehlersuche verbracht.

    Ursache war in der Tat ein falsch gesetzter Primärschlüssel. Nun funktioniert es! :-)

    Vielen Dank für den 'Denkanstoß'

    Grüße Gerhard

Neuen Kommentar schreiben

Hinweis: Name und Kommentar werden gespeichert und auf der Website angezeigt. Die E-Mail-Adresse wird nicht veröffentlicht. Sie dient nur dazu, ggf. eine Frage zu beantworten, und wird anschließend gelöscht. Für weitere Infos lesen Sie bitte unsere » Datenschutzerklärung.