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