2 Die Fehlermeldung
Wenn man in einem Query Editor eine SQL Abfrage baut und dann ausführt, bekommt man vielleicht eine Fehlermeldung zurück:
Query:
SELECT*
FROM [dbo].[GibtsNicht];
GO
Meldung:
Msg 208, Level 16, State 1, Line 1
Invalid object name 'dbo.GibtsNicht'.
Tritt derselbe Fehler in einer Stored Procedure auf, sieht die Fehlermeldung fast gleich aus, enthält aber zusätzlich noch den Namen der Prozedur, in welcher der Fehler aufgetreten ist:
Msg 208, Level 16, State 1, Procedure dbo.usp_CauseError, Line 4 [Batch Start Line 6]
Invalid object name 'dbo.NonExistingTable'.
Diese Meldungen bestehen aus folgenden Teilen:
3 Fehlerfall und Abbruch Typen
Wenn in einem einzelnen Befehl ein Fehler auftaucht, scheitert dieser – Ende der Geschichte. Interessanter wird es, wenn irgendwo in einer ganzen Reihe von Befehlen einer fehlschlägt: Was passiert mit den Befehlen, die bereits gelaufen sind, und was soll mit denen geschehen, die noch nicht gelaufen sind?
Damit wir was zum Testen haben, bauen wir uns eine einfache Tabelle:
CREATETABLE [dbo].[Product]
(
[ProductNumber] NVARCHAR(50)NOTNULLPRIMARYKEY,
[ProductName] NVARCHAR(50)NOTNULL,
[Stock] INTNOTNULLCHECK ([Stock] >=0)
);
GO
Fügen wir da mal den ersten, fehlerfreien Datensatz ein:
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.001','PRS SE Soapbar II', 1);
GO
Ja, hat geklappt, denn die Antwort ist:
(1 row affected)
Und so sieht jetzt der Inhalt der Tabelle aus:
So einfach diese Tabelle ist, sie bietet eine Fülle von Möglichkeiten Fehler zu machen, wenn man da Daten einfügen oder verändern will:
- Irgend eines der Felder bekommt keinen Wert.
- Werte in den ersten beiden Feldern kommen mit mehr als 50 Zeichen.
- Ein bereits bestehender Wert soll nochmals in ProductNumber eingefügt werden.
- Ein negativer oder ein nicht numerischer Wert kommt für die Spalte Stock daher.
Machen wir mal ein paar davon.
3.1 Statement Termination
Hier ist ein kleiner Batch, dessen zweiter Befehl einen Fehler verursachen wird (Wert für Stock negativ):
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster',-1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
GO
Und das ist die Meldung, die wir darauf bekommen:
(1 row affected)
Msg 547, Level 16, State 0, Line 3
The INSERT statement conflicted with the CHECK constraint "CK__Product__Stock__25869641".
The conflict occurred in database "rca.DemoDB", table "dbo.Product", column 'Stock'.
The statement has been terminated.
(1 row affected)
Diese Meldungen sagen uns Folgendes:
- Der erste Befehl hat funktioniert und eine Zeile in die Datenbank eingefügt.
- Der zweite Befehl ist gescheitert.
- Der dritte Befehl hat wieder funktioniert und eine Zeile in die Datenbank eingefügt.
Der Inhalt unserer Tabelle sieht jetzt so aus:
Wenn SQL Server in einem Batch auf einen Befehl stösst, der einen Fehler verursacht, dann wird dieser eine Befehl, beziehungsweise das Statement, abgebrochen, respektive terminiert. Alles andere läuft durch als wäre nichts geschehen.
Dieser Umgang mit Fehlern wird Statement Termination genannt.
3.2 Batch Abort
Damit das Batch Abort Beispiel möglichst vergleichbar mit dem aus Statement Termination ist, versetzen wir die Tabelle wieder in denselben Zustand wie vor dem letzten Beispiel und löschen alles, ausser dem ersten Datensatz:
DELETE
FROM [rca.DemoDB].[dbo].[Product]
WHERE [ProductNumber] >'100.001';
GO
Jetzt führen wir denselben Batch nochmals aus, mit dem kleinen Unterschied, dass ganz am Anfang des Scripts der Befehl SET XACT_ABORT ON hingeschrieben wird:
SETXACT_ABORTON;
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster',-1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
GO
Jetzt sieht die Antwort so aus:
(1 row affected)
Msg 547, Level 16, State 0, Line 4
The INSERT statement conflicted with the CHECK constraint "CK__Product__Stock__25869641".
The conflict occurred in database "rca.DemoDB", table "dbo.Product", column 'Stock'.
Sieht genau gleich aus wie die Meldung bei Statement Termination, nur dass die letzte Zeile dort hier fehlt – nach der Fehlermeldung steht nicht, dass das Statement terminiert worden ist und wir sehen keine zweite (1 row affected) Meldung.
Das hat schon seinen Grund, denn wenn wir den Tabelleninhalt jetzt anschauen, sieht der so aus:
Der erste Datensatz aus unserem Batch hat's noch in die Tabelle geschafft, der zweite schlug dann fehl, woraufhin SQL Server nicht nur diesen einen Befehl, sondern die ganze weitere Verarbeitung des Batches abgebrochen hat.
Dieses Verhalten nennt man Batch Abort.
3.3 Conversion Error
Wiederum löschen wir alles bis auf den ersten Datensatz aus der Tabelle und schalten XACT_ABORT wieder aus, indem wir die Lösch-Query um einen zusätzlichen Befehl erweitern:
DELETE
FROM [rca.DemoDB].[dbo].[Product]
WHERE [ProductNumber] <>'100.001';
GO
SETXACT_ABORTOFF;
Auf das Ausführen dieser kleinen Query hier beziehen wir uns künftig mit Tabelle vorbereiten.
Jetzt nehmen wir wieder unsere drei Insert Statements und versuchen im zweiten Datensatz einen String in ein Integer zu konvertieren.
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster',CAST('TWELVE'ASint));
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
GO
Hier die überraschende Antwort:
(1 row affected)
Msg 245, Level 16, State 1, Line 3
Conversion failed when converting the varchar value 'TWELVE' to data type int.
Erste Zeile verarbeitet, Fehler, dann nichts mehr. Das finden wir jetzt in der Tabelle vor:
Ein Conversion Error in einem Batch ohne XACT_ABORT ON resultiert also nicht in einer Statement Termination, sondern wie mit XACT_ABORT ON in einem Batch Abort.
3.4 Transaction
Nachdem die Tabelle wieder vorbereitet worden ist, packen wir unsere Insert Statements (ohne Conversion aber wieder mit einem ungültigen Wert für Stock) mal in eine Transaction:
BEGINTRAN;
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster',-1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
COMMITTRAN;
GO
Die Antwort auf diesen Befehl ist
(1 row affected)
Msg 547, Level 16, State 0, Line 4
The INSERT statement conflicted with the CHECK constraint "CK__Product__Stock__25869641".
The conflict occurred in database "rca.DemoDB", table "dbo.Product", column 'Stock'.
The statement has been terminated.
(1 row affected)
Das sieht wieder so aus wie beim ersten Test, als XACT_ABORT nicht eingeschaltet war (ist ja auch jetzt nicht eingeschaltet):
- Der erste Befehl hat funktioniert und eine Zeile in die Datenbank eingefügt.
- Der zweite Befehl ist gescheitert.
- Der dritte Befehl hat wieder funktioniert und eine Zeile in die Datenbank eingefügt.
Auch die Tabelle sieht jetzt natürlich genau gleich aus:
3.5 Transaction mit XACT_ABORT
Nachdem die Tabelle wieder vorbereitet ist, setzen wir SETXACT_ABORTON vor die Transaction und führen sie nochmals aus.
SETXACT_ABORTON;
BEGINTRAN;
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster',-1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
COMMITTRAN;
GO
Die Antwort auf diese Query:
(1 row affected)
Msg 547, Level 16, State 0, Line 5
The INSERT statement conflicted with the CHECK constraint "CK__Product__Stock__25869641".
The conflict occurred in database "rca.DemoDB", table "dbo.Product", column 'Stock'.
Dies sieht aus wie beim zweiten Beispiel – eine Zeile verarbeitet, dann Abbruch.
Wenn wir jetzt aber in die Tabelle schauen, sehen wir das hier:
Gar nichts wurde verarbeitet.
XACT_ABORT hat hier innerhalb des Batches einen Batch Abort verursacht und innerhalb einer Transaktion resultiert ein Batch Abort in einem Roll-Back der ganzen Transaktion.
3.6 Transaction mit Conversion Error
Mit einer frisch vorbereiteten Tabelle packen wir den Batch mit dem Conversion Error in eine Transaktion (XACT_ABORT ist jetzt ausgeschaltet):
BEGINTRAN;
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster',CAST('TWELVE'ASint));
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
COMMITTRAN;
GO
Darauf kriegen wir folgende Meldungen:
(1 row affected)
Msg 245, Level 16, State 1, Line 4
Conversion failed when converting the varchar value 'TWELVE' to data type int.
Und so sieht's in unserer Tabelle aus:
Wie wir schon wissen, führt ein Conversion Error innerhalb eines Batches zu einem Batch Abort, und wie wir ebenfalls wissen, führt ein Batch Abort innerhalb einer Transaktion zu einem Roll-Back der ganzen Transaktion. Genau das ist hier passiert.
4 Try – Catch
Try – Catch in SQL funktioniert ähnlich wie das selbe Konstrukt in C#: eine Anzahl Statements (eines oder mehrere) werden in einen Try Block gepackt und wenn dort drin ein Fehler passiert, übernimmt der Catch Block, indem der aufgetauchte Fehler behandelt werden kann.
4.1 Kein Fehler im Try Block
Passiert kein Fehler im Try Block, wird der Catch Block gar nie ausgeführt, wie in folgendem Beispiel gezeigt (Tabelle natürlich wieder vorbereitet):
BEGINTRY
PRINT'Start TRY';
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
PRINT'End TRY';
ENDTRY
BEGINCATCH
PRINT'In CATCH';
ENDCATCH
GO
Hier die Meldungen:
Start TRY
(1 row affected)
(1 row affected)
(1 row affected)
End TRY
Wir sehen, alle drei Inserts innerhalb des Try Blocks wurden erfolgreich ausgeführt und der Catch Block wurde nicht aufgerufen.
Hier die Tabelle (zum ersten Mal mit vier Records):
4.2 Statement Termination im Try Block
Interessant wir das Try – Catch Konstrukt natürlich erst, wenn im Try Block ein Fehler passiert.
Wenn nach Vorbereitung der Tabelle dieses Script hier ausgeführt wird (zweiter Insert mit ungültigem Wert für Stock)…
BEGINTRY
PRINT'Start TRY';
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster',-1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
PRINT'End TRY';
ENDTRY
BEGINCATCH
PRINT'In CATCH';
ENDCATCH
GO
… kriegen wir diese Meldungen:
Start TRY
(1 row affected)
(0 rows affected)
In CATCH
Und das ist in der Tabelle:
Der erste Insert hat geklappt. Beim zweiten ist ein Fehler aufgetreten, der für sich alleine eine Statement Termination verursacht und wir sehen, dass innerhalb eines Try Blocks ein solcher Fehler zu einem Batch Abort führt.
Da die Kontrolle beim Auftauchen eines Fehlers sofort dem Catch Block übergeben wird, erscheint keine Fehlermeldung in den Meldungen – darum soll sich der Catch Block kümmern.
4.3 Transaction im Try Block
Es spricht nichts dagegen, einen Batch von Statements innerhalb eines Try Blocks in eine Transaktion zu packen.
Da wir nun eine Transaktion haben, können wir im Catch Block etwas damit machen:
BEGINTRY
PRINT'Start TRY';
BEGINTRAN;
PRINT'Start TRAN';
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster',-1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
PRINT'End TRAN';
COMMITTRAN;
PRINT'End TRY';
ENDTRY
BEGINCATCH
PRINT'Start CATCH';
IF(@@TRANCOUNT> 0)
ROLLBACKTRAN;
PRINT'End CATCH';
ENDCATCH
GO
Im Catch Block wird überprüft, ob eine Transaktion aktiv ist und wenn ja, wird darauf ein Roll-Back gemacht.
Wird diese Query nach Vorbereiten der Tabelle ausgeführt, werden diese Meldungen ausgegeben:
Start TRY
Start TRAN
(1 row affected)
(0 rows affected)
Start CATCH
End CATCH
Wir sehen also…
- Der Try Block fängt an.
- Die Transaktion fängt an.
- Der erste Insert wird ausgeführt.
- Der zweite verursacht einen Fehler.
- Der Catch Block übernimmt und läuft durch.
Die Tabelle präsentiert sich nun so:
Gar nichts ist passiert, weil in dem Catch Block ein Roll-Back gemacht wurde.
4.4 Auswertung der Fehlerinformationen im Catch Block
Wie wir im Abschnitt Fehlermeldung gesehen haben, sind Fehlermeldungen in SQL aus verschiedenen Teilen aufgebaut und diese können in dem Catch Block ausgewertet werden.
Machen wir das mal, indem wir den Catch Block die einzelnen Teile der Meldung einfach mal ausgeben lassen, bevor der Roll-Back ausgeführt wird (der Try Block ist unverändert vom letzten Beispiel übernommen):
BEGINCATCH
PRINT'Start CATCH';
PRINT'Fehler Code : '+CAST(ERROR_NUMBER()ASVARCHAR)
PRINT'Schweregrad : '+CAST(ERROR_SEVERITY()ASVARCHAR)
PRINT'Status : '+CAST(ERROR_STATE()ASVARCHAR)
PRINT'Code Zeile : '+CAST(ERROR_LINE()ASVARCHAR)
PRINT'Beschreibung: '+ERROR_MESSAGE()
IF(@@TRANCOUNT> 0)
ROLLBACKTRAN;
PRINT'End CATCH';
ENDCATCH
Die Meldungen:
Start TRY
Start TRAN
(1 row affected)
(0 rows affected)
Start CATCH
Fehler Code : 547
Schweregrad : 16
Status : 0
Code Zeile : 9
Beschreibung: The INSERT statement conflicted with the CHECK constraint "CK__Product__Stock__25869641". The conflict occurred in database "rca.DemoDB", table "dbo.Product", column 'Stock'.
End CATCH
Das ist vor allem dann nützlich, wenn man je nach Fehlersituation unterschiedlich reagieren will im Catch Block.
Nehmen wir mal an, dass Einfügefehler wegen ungültiger Werte für Stock periodisch nachbearbeitet werden und daher die Verarbeitung der Statements vor dem Fehler nicht beeinträchtigt werden soll, bei anderen Fehlern aber die gesamte Transaktion nicht ausgeführt werden soll – wenn zum Beispiel eine Schlüsselverletzung auftritt, muss erst auf manuellem Wege festgestellt werden, wieso das passiert ist, bevor korrigiert werden kann.
Ändern wir bei gleichbleibendem Try Block den Catch Block folgendermassen ab:
BEGINCATCH
PRINT'Start CATCH';
IFCAST(ERROR_NUMBER()ASVARCHAR)='547'ANDCHARINDEX('column ''Stock''',ERROR_MESSAGE())> 0
BEGIN
IF(@@TRANCOUNT> 0)
BEGIN
PRINT'Commit';
COMMITTRAN;
END
END
ELSE
BEGIN
IF(@@TRANCOUNT> 0)
BEGIN
PRINT'Rollback';
ROLLBACKTRAN;
END
END
PRINT'End CATCH';
ENDCATCH
Hier die Meldungen:
Start TRY
Start TRAN
(1 row affected)
(0 rows affected)
Start CATCH
Commit
End CATCH
Die Tabelle:
Im Catch Block wurde der Ast mit dem COMMIT TRAN Statement durchlaufen, die Transaktion innerhalb des Try Blockes wurde ausgeführt und innerhalb eines Try Blockes resultiert eine Statement Termination in einem Batch Abort, und das haben wir hier:
- Der erste Befehl hat funktioniert und eine Zeile in die Datenbank eingefügt.
- Der zweite Befehl ist gescheitert und die Batchverarbeitung wurde abgebrochen.
Jetzt lassen wir mal alles so stehen in der Query, aber wir bauen im zweiten Insert Statement einen anderen Fehler ein: Wert für Stock ist jetzt korrekt, aber für die ProductNumber (Primary Key) kommt nun ein Wert daher, der bereits in der Tabelle vorhanden ist.
Hier der neue Try Block:
BEGINTRY
PRINT'Start TRY';
BEGINTRAN;
PRINT'Start TRAN';
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.001','Fender Stratocaster', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
PRINT'End TRAN';
COMMITTRAN;
PRINT'End TRY';
ENDTRY
Der Catch Block, wie gesagt, ist gleich wie beim Beispiel vorhin.
Wenn diese Query ausgeführt wird (nachdem die Tabelle vorbereitet worden ist), bekommen wir folgende Meldungen:
Start TRY
Start TRAN
(1 row affected)
(0 rows affected)
Start CATCH
Rollback
End CATCH
Diesmal wurde im Catch Block der Ast mit dem Roll-Back durchlaufen und somit wurde kein Befehl in der Transaktion ausgeführt.
5 Übersicht Abbruchverhalten
6 THROW
Mit THROW können innerhalb einer Query, zum Beispiel innerhalb eines Catch Blocks, Fehler geworfen werden.
6.1 'Re'-THROW
Wie wir bereits gesehen haben, 'verschluckt' ein Catch Block sozusagen den Fehler mitsamt der Fehlermeldung, was sich darin zeigt, dass keine Fehlermeldung in den Meldungen erscheint, wenn ein Try – Catch Konstrukt verwendet wird. Man kann den Fehler, der dem Catch Block übergeben wurde, nochmals werfen, indem man THROW ohne Parameter verwendet:
Der Catch Block:
BEGINCATCH
PRINT'Start CATCH';
THROW
PRINT'End CATCH';
ENDCATCH
GO
Die Meldungen:
Start TRY
(1 row affected)
(0 rows affected)
Start CATCH
Msg 547, Level 16, State 0, Line 6
The INSERT statement conflicted with the CHECK constraint "CK__Product__Stock__25869641".
The conflict occurred in database "rca.DemoDB", table "dbo.Product", column 'Stock'.
Das bedeutet:
- Der Try Block fängt an.
- Ein Insert wird ausgeführt.
- Der zweite Insert verursacht einen Fehler und der CATCH Block wird aktiv.
- Der CATCH Block wirft den Fehler nochmals und bricht ab.
Wir sehen, der Try Block wird verlassen, sobald der zweite Insert den Fehler verursacht und die Kontrolle geht an den Catch, und der Catch Block wird verlassen nachdem er mit THROW den Fehler nochmals geworfen hat.
6.2 'Custom'-THROW
Wie wir schon wissen, erhält der Catch Block sämtliche Informationen über den Fehler, der im Try Block aufgetaucht ist. Es ist nun möglich, aufgrund dieser Informationen eigene Fehler zu schmeissen, indem der THROW Anweisung entsprechende Parameter mitgegeben werden.
Die Syntax dazu ist wie folgt aufgebaut:
THROW [Fehler Code, Beschreibung, Status]
Für anwenderdefinierte Fehler können Fehler Codes über 50000 verwendet werden (Codes bis 50000 sind für Systemfehler reserviert), Beschreibung ist selbsterklärend und Status kann verwendet werden, um später die Stelle im Script, an welcher der Fehler geworfen wurde, leichter aufzufinden.
Ein Custom-THROW könnte also ungefähr so aussehen, wie im Catch Block des folgenden Scripts gezeigt:
BEGINTRY
PRINT'Start TRY';
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.002','Ibanez Artist', 1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.003','Fender Stratocaster',-1);
INSERTINTO [dbo].[Product]([ProductNumber], [ProductName], [Stock])
VALUES ('100.004','Gibson Explorer', 1);
PRINT'End TRY';
ENDTRY
BEGINCATCH
PRINT'Start CATCH';
IFCAST(ERROR_NUMBER()ASVARCHAR)='547'ANDCHARINDEX('column ''Stock''',ERROR_MESSAGE())> 0
THROW 50100,'Invalid value for ''Stock''', 0
ELSE
THROW
PRINT'End CATCH';
ENDCATCH
GO
Wenn bestimmte Bedingungen im Fehler erfüllt sind (Fehler Code 547 und in der Beschreibung muss irgendwo der Spaltenname Stock vorkommen), wird der benutzerdefinierte Custom-THROW ausgeführt, ansonsten geschieht ein einfacher Re-THROW.
Hier die Meldungen, wenn der Custom-THROW zum Zuge kommt:
Start TRY
(1 row affected)
(0 rows affected)
Start CATCH
Msg 50100, Level 16, State 0, Line 18
Invalid value for 'Stock'
Wir sehen, für Schweregrad (Level) und die Zeilennummer im Code (Line) werden die Werte aus dem ursprünglichen Fehler genommen, und für den Rest werden die benutzerdefinierten Parameterwerte verwendet. Wie beim Re-THROW, wird auch beim Custom-THROW der Catch Block unmittelbar nach dem THROW verlassen.
Robert Cavigelli, Lead Developer QUIBIQ Schweiz AG