Ausgabeparameter nicht gesetzt, wenn die gespeicherte Prozedur in einem TRY / CATCH fehlschlägt


7

In SQL Server 2008 (aber auch 2014). Betrachten wir eine Prozedur mit einem Ausgabeparameter. Diese Prozedur kann einen Fehler erzeugen (und wird im folgenden Beispiel auftreten). Ich stelle fest, dass das Verhalten des Ausgabeparameters nicht dasselbe ist, wenn wir die Prozedur innerhalb eines TRY/ CATCHBlocks aufrufen .

Beispiel:

create procedure test_output @result varchar(10) output
as
begin
    set @result = 'hello'
    raiserror('This is an error', 16,1)
    set @result = 'error'
end

Wenn wir das Verfahren auf einfache Weise starten:

declare @res1 varchar(10)
exec test_output @result = @res1 out
print 'Result is: '+ isnull(@res1, 'empty')

wir bekommen (und ich bin damit einverstanden):

Meldung 50000, Ebene 16, Status 1, Prozedur test_output, Zeile 7 [Stapelstartzeile 12]
Dies ist ein Fehler.
Ergebnis ist: Fehler

Wenn sich die Prozedur jetzt in einem Try / Catch-Block befindet:

declare @res2 varchar(10)
declare @error_message varchar(max)
begin try
    exec test_output @result = @res2 out
end try
begin catch
    set @error_message = error_message()
    raiserror(@error_message, 16,1)
    print 'Result is: '+ isnull(@res2, 'empty')
end catch

wir bekommen (und ich bin verärgert):

Meldung 50000, Ebene 16, Status 1, Zeile 28
Dies ist ein Fehler.
Ergebnis ist: leer

Die Fehlermeldung ist OK, aber der Ausgabeparameter ist jetzt NULL . Wenn in einem TRY...CATCHKontext die Ausführung unmittelbar nach dem angehalten wird RAISERROR, hätte ich erwartet, dass der Ausgabewert auf Hallo gesetzt wird .

Wieso ist es so?

Antworten:


5

Sie haben einen guten Start mit diesem Testaufbau, aber es fehlt etwas, das Sie dazu veranlasst, falsch zu interpretieren, was tatsächlich passiert. Wenn Sie PRINTam Anfang, in der Mitte und am Ende der gespeicherten Prozedur Anweisungen eingeben, wird durch die zusätzliche Ausgabe klarer, was hier vor sich geht. Zum Beispiel:

USE [tempdb];
GO
CREATE PROCEDURE test_output
(
  @Result VARCHAR(10) OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;

    SET @Result = 'hello';

    PRINT 1;
    RAISERROR('This is an error', 16, 1);

    PRINT 2;
    SET @Result = 'error';

    PRINT 3;
END;
GO

Die Ausgabe Ihrer ersten Testabfrage lautet:

1
Msg 50000, Level 16, State 1, Procedure test_output, Line xxxx [Batch Start Line yyyyy]
This is an error
2
3
Result is: error

Und das haben Sie wahrscheinlich sowieso erwartet. Die Ausgabe der zweiten Testabfrage lautet jedoch:

1
Msg 50000, Level 16, State 1, Line xxxxx
This is an error
Result is: empty

Das ist ganz anders. Wir können jetzt sehen, dass innerhalb des TRY...CATCHKonstrukts die Ausführung sofort nach dem RAISERRORAufruf angehalten wird (dh es wird ein Batch-Abbruch-Ereignis). Hält andererseits RAISERRORdie Ausführung nicht sofort an, wenn sie nicht innerhalb eines TRY...CATCHKonstrukts aufgerufen wird .

Wie Sie in Ihrem Update der Frage ausgeführt haben, erklärt dies jedoch nicht, warum der OUTPUTParameter dann nicht auf gesetzt ist hello. Dies liegt an der Absicht des normalen Verhaltens gespeicherter Prozeduren, die teilweise Ausführung nicht widerzuspiegeln (aufgrund eines Batch-Abbruchfehlers). Dies wird im folgenden Blog-Beitrag besprochen:

TSQL-Grundlagen II - Parameterübergabesemantik

Das heißt: Obwohl die gespeicherte Prozedur den Schritt ausgeführt hat, auf den die Variable gesetzt wurde hello, RAISERRORist dies jetzt ein Fehler beim Batch-Abbruch, wenn sie innerhalb eines TRY...CATCHKonstrukts aufgerufen wird , und gespeicherte Prozeduren spiegeln keine Änderungen an Parametern wider, wenn sie abgebrochen werden.

Dieses Verhalten steht auch im Mittelpunkt der folgenden Erklärung:

Warum müssen TVPs BEREIT sein und warum können Parameter anderer Typen nicht BEREIT sein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.