SQL Server: Fehlerbehandlung in DAO und ADODB

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

Die Fehlerbehandlung in VBA ist relativ einfach zu handhaben, sobald man einmal die grundlegenden Techniken kennt. Nicht viel anders sollte es eigentlich sein, wenn man die Daten nicht mehr aus Access-Tabellen bezieht, sondern aus SQL Server-Tabellen. Es gibt jedoch einige Unterschiede, die man kennen sollte. In diesem Artikel zeigen wir, welche Besonderheiten auftreten, wenn man nicht mehr auf Access-Tabellen zugreift, sondern auf verknüpfte SQL Server-Tabellen und dabei die Datenzugriffstechniken DAO- und ADODB nutzt. Im letzteren Fall gibt es sogar noch Unterschiede bezüglich der verwendeten Treiber/Provider.

Wenn wir mit VBA programmieren und es wird ein Laufzeitfehler ausgelöst, liefert dieser immer genau ein Fehlerobjekt namens Err zurück, das uns mit Eigenschaften wie Number oder Descriptionen Informationen über den Fehler liefert.

Wenn wir mit DAO auf die Daten eines SQL Servers oder einer anderen per ODBC-Tabellenverknüpfung eingebundenen Datenbanksystems zugreifen, erhalten wir ebenfalls eine Fehlermeldung mit Err.

Allerdings lautet diese immer gleich – nämlich den Fehler ODBC-Aufruf fehlgeschlagen mit der Nummer 3146.

Damit kann man eigentlich nichts anfangen – da dieser Fehler immer in Zusammenhang mit dem Datenzugriff auftritt, wissen wir zwar grob, worum es geht, aber nicht genau, was der eigentliche Fehler ist.

Man kann zwar nun die auslösende SQL-Anweisung kopieren, sie im SQL Server Management Studio ausführen und erhält dann dort die eigentliche Fehlermeldung. Das hilft uns allerdings nicht, wenn wir im VBA-Code darauf reagieren wollen.

In diesem Artikel stellen wir Dir eine Möglichkeit vor, wie Du dennoch auf die Fehlerinformationen zugreifen kannst. Dazu gibt es in DAO und ADODB verschiedene Möglichkeiten.

Unterscheidung der verschiedenen Fehler-Klassen

Zuerst jedoch ein kleiner Blick auf das “normale” Err-Objekt. Dieses hat den Typ ErrObject und bietet die folgenden Eigenschaften und Methoden.

Hier zunächst die Eigenschaften:

  • Description: Enthält die Fehlerbeschreibung als String.
  • HelpContext: Gibt die ID des Hilfethemas zurück, das mit dem Fehler verbunden ist.
  • HelpFile: Gibt den Pfad zur zugehörigen Hilfedatei zurück (falls vorhanden).
  • LastDLLError: Gibt den letzten Windows-API-Fehlercode zurück (nur bei API-Aufrufen relevant).
  • Number: Gibt die Fehlernummer zurück. 0 bedeutet, dass kein Fehler aufgetreten ist. Bei eingebauten Elementen sind die Fehlernummern positiv und ein- bis fünfstellig. Es gibt aber auch Fehlermeldungen mit großen negativen Fehlernummern.
  • Source: Gibt die Quelle des Fehlers zurück (zum Beispiel VBAProject).

Und hier sind die beiden Methoden:

  • Clear: Setzt alle Fehlerinformationen zurück (Number = 0, Description = “”, et cetera).
  • Raise: Erzeugt einen benutzerdefinierten Fehler mit einer bestimmten Fehlernummer.

Unter DAO gibt es dagegen wesentlich weniger Elemente. Die Fehlerklasse heißt hier auch nicht ErrObject, sondern Error. Hier gibt es nur (Beschreibung wie bei VBA):

  • Description
  • Number
  • Source

Unter ADO finden wir wieder fast die gleichen Eigenschaften wie unter VBA, nämlich Description, HelpContext, HelpFile, Number und Source. Wir finden aber auch zwei weitere Eigenschaften:

  • NativeError: Liefert uns die Server-spezifische Fehlernummer. So erhalten wir beispielsweise für den gleichen Fehler im VBA-Error die Nummer -2147217873 und im ADODB-Error die Nummer 547.
  • SQLState: Gibt den SQL-Fehlercode im standardisierten 5-stelligen Zeichenformat zurück. Diese Codes folgen dem SQL-92-Standard und sind von der Datenbank unabhängig.

Fehler beim Zugriff mit DAO auswerten

Wenn beim Zugriff auf eine SQL Server-Datenbank über eine Tabellenverknüpfung oder andere Zugriffe per ODBC Fehler auftreten, können wir diese mit der herkömmlichen Err-Klasse nur bedingt analysieren.

Der Grund ist, dass wir mit der Err-Klasse nur Informationen zum letzten aufgetretenen Fehler erhalten. Das ist auch so, wenn wir per Tabellenverknüpfung auf eine per ODBC verknüpfte Datenbank zugreifen. Hier können wir dann jedoch die Errors-Auflistung von DAO hinzuziehen. Hier finden wir jedoch noch ein weiteres Problem vor, nämlich dass die Err-Klasse immer nur eine generische Meldung liefert, die keinen Hinweis auf den tatsächlichen Fehler enthält. Die folgenden Anweisungen sollten beispielsweise einen Fehler auslösen, weil wir dem Feld AnlageID keinen Wert zuweisen können:

Dim db As DAO.Database
Dim rst As DAO.Recordset
Set db = CurrentDb
db.Execute "INSERT INTO tblAnlagen(AnlageID) VALUES(1)", dbFailOnError + dbSeeChanges

Wir erhalten dafür jedoch lediglich die Fehlermeldung aus Bild 1.

Fehler beim Ausführen einer SQL Server-Abfrage

Bild 1: Fehler beim Ausführen einer SQL Server-Abfrage

Fehler mit DAO.Errors auswerten

Es gibt jedoch nicht nur das Err-Objekt, um Fehler auszuwerten. Wir können auch die DAO.Errors-Auflistung bemühen, um den tatsächlich vom SQL Server gelieferten Fehler zu erhalten. Diese liefert uns Elemente des gleichen Typs wie unser bekanntes Error-Objekt. Diese können wir in einer Schleife über die Errors-Auflistung der DAO-Klasse durchlaufen:

For i = 0 To DAO.Errors.Count
     Debug.Print i, DAO.Errors(i).Description
Next i

Wir schauen uns diese Codezeilen einmal im Zusammenhang mit einer Prozedur an, die einen entsprechenden Fehler auslöst (siehe Listing 1). Hier erhalten wir die Fehlermeldungen, wie sie auch im SQL Server angezeigt werden:

Public Sub DAOFehlerAnalysieren()
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Dim i As Integer
     Set db = CurrentDb
     On Error Resume Next
     db.Execute "INSERT INTO tblAnlagen(AnlageID) VALUES(1)", dbFailOnError + dbSeeChanges
     Debug.Print db.RecordsAffected, Err.Number
     Debug.Print "DAO.Errors:"
     For i = 0 To DAO.Errors.Count
         Debug.Print i, DAO.Errors(i).Description
     Next i
End Sub

Listing 1: Analysieren von Fehlern beim DAO-Zugriff auf ODBC-Verbindungen

[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Verletzung der PRIMARY KEY-Einschränkung "tblAnlagen$PrimaryKey". Ein doppelter Schlüssel kann in das dbo.tblAnlagen-Objekt nicht eingefügt werden. Der doppelte Schlüsselwert ist (1).
[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Die Anweisung wurde beendet.
ODBC-Aufruf fehlgeschlagen.

Fehlerbehandlung unter ADODB

Das ist bei Fehlern bei Datenbankzugriffen mit ADODB bereits etwas besser gelöst – hier erhalten wir eine aussagekräftige Fehlermeldung. Wir lösen in Listing 2 den gleichen Fehler aus wie in der DAO-Version, nur diesmal über die Execute-Methode der Connection-Klasse.

Public Function FehlerAnalysieren()
     Dim cnn As ADODB.Connection
     Dim strConnection As String
     Dim strSQL As String
     Dim objError As ADODB.Error
     Set cnn = New ADODB.Connection
     strConnection = "Provider=MSOLEDBSQL;Server=amvDesktop2023;Trusted_Connection=yes;Database=Anlagen"
     strSQL = "INSERT INTO tblAnlagen(AnlageID) VALUES(1)"
     cnn.ConnectionString = strConnection
     cnn.Open
     On Error Resume Next
     cnn.Execute strSQL
     Debug.Print "Err.Number: " & Err.Number
     Debug.Print "Err.Description: " & Err.Description
     If Not Err.Number = 0 Then
         For Each objError In cnn.Errors
             With objError
                 Debug.Print "Number:          " & .Number
                 Debug.Print "Description:     " & .Description
                 Debug.Print "NativeError:     " & .NativeError
                 Debug.Print "Source:          " & .Source
                 Debug.Print "SQLState:        " & .SQLState
             End With
         Next objError
     End If
     cnn.Close
End Function

Listing 2: Beispiel für einen SQL Server-Fehler

Bereits für Err.Number und Err.Description erhalten wir diese Meldung:

Err.Number: -2147217873
Err.Description: Ein expliziter Wert für die Identitätsspalte kann nicht in der tblAnlagen-Tabelle eingefügt werden, wenn IDENTITY_INSERT auf OFF festgelegt ist.

Die Ausgabe des Fehlers der Errors-Auflistung des Connection-Objekts liefert folgende Informationen:

Number:          -2147217873
Description:     Ein expliziter Wert für die Identitätsspalte kann nicht in der tblAnlagen-Tabelle eingefügt werden, wenn IDENTITY_INSERT auf OFF festgelegt ist.
NativeError:     544
Source:          Microsoft OLE DB Driver for SQL Server
SQLState:        23000

Unter Number und Description erhalten wir die gleichen Informationen wir über das Err-Objekt.

Mit NativeError erhalten wir die SQL Server-interne Fehlernummer. Alle Fehlernummern und Fehlerbeschreibungen des SQL Servers in deutscher Sprache können wir uns übrigens mit der folgenden Anweisung, abgesetzt im SQL Server Management Studio, anzeigen lassen:

SELECT * FROM sys.messages WHERE language_id = 1031;

Source liefert die Fehlerquelle, in diesem Fall den Treiber, der hinter dem Provider MSOLEDBSQL steckt. Und SQLState liefert einen Zahlenwert, der wiederum einen ODBC-Fehlercode enthält. Diese Fehlercodes können wir beispielsweise auf der folgende Seite nachlesen:

https://learn.microsoft.com/en-us/sql/odbc/reference/appendixes/appendix-a-odbc-error-codes?view=sql-server-ver15

Anderenfalls sollten diese unter dem Schlüsselwort ODBC Error Codes zu finden sein.

Für den Zahlencode 23000 finden wir hier beispielsweise die Information Integrity constraint violation.

Benutzerdefinierte Fehler in gespeicherten Prozeduren

Um das nächste Beispiel sowohl mit DAO als auch mit ADODB nachvollziehen zu können, legen wir eine gespeicherte Prozedur in einer SQL Server-Datenbank an, die wie in Listing 3 aussieht.

CREATE PROCEDURE Beispielfehlerprozedur_EinFehler
AS  
BEGIN  
     SET NOCOUNT ON;  
     BEGIN TRY  
         -- Ein Fehler wird absichtlich erzeugt (Division durch Null)
         DECLARE @x INT = 1 / 0;  
     END TRY  
     BEGIN CATCH  
         -- Fehlerinformationen abrufen
         DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();  
         DECLARE @ErrorSeverity INT = ERROR_SEVERITY();  
         DECLARE @ErrorState INT = ERROR_STATE();  
         -- Fehler zurückgeben
         RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);  
     END CATCH  
END;

Listing 3: Erzeugen eines Fehlers in einer gespeicherten Prozedur

Die CREATE PROCEDURE-Anweisung verwendet die TRY…CATCH-Struktur von T-SQL. Im TRY-Block, der mit BEGIN TRY eingeleitet wird, weisen wir einer Variablen das Ergebnis einer Berechnung zu, die durch das Teilen von 1 durch 0 einen Fehler auslöst.

Im CATCH-Block deklarieren wir die drei Variablen @ErrorMessage, @ErrorSeverity und @ErrorState und weisen diesen die Ergebnisse der Funktionen ERROR_MESSAGE(), ERROR_SEVERITY() und ERROR_STATE() zu.

Schließlich rufen wir die RAISEERROR-Methode auf und übergeben dieser die drei zuvor festgelegten Variablen als Parameter. Durch das Ausführen der CREATE PROCEDURE-Anweisung wird die gespeicherte Prozedur erstellt.

Dann rufen wir diese gespeicherte Prozedur mit der folgenden Anweisung auf:

EXEC Beispielfehlerprozedur_EinFehler

Damit erhalten wir eine Fehlermeldung, die wie eine herkömmlich ausgelöste Fehlermeldung aussieht (siehe Bild 2).

Ausführen einer SQL Server-Abfrage im SQL Server Management Studio

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

Testzugang

eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar