2009-06-29 5 views
25

Ich habe eine gespeicherte Prozedur, die eine Parametervalidierung durchführt und fehlschlagen und die Ausführung stoppen sollte, wenn der Parameter nicht gültig ist.Der "richtige" Weg zur Überprüfung der gespeicherten Prozedurparameter

Mein erster Ansatz für die Fehlerprüfung sah wie folgt aus:

create proc spBaz 
(
    @fooInt int = 0, 
    @fooString varchar(10) = null, 
    @barInt int = 0, 
    @barString varchar(10) = null 
) 
as 
begin 
    if (@fooInt = 0 and (@fooString is null or @fooString = '')) 
    raiserror('invalid parameter: foo', 18, 0) 

    if (@barInt = 0 and (@barString is null or @barString = '')) 
    raiserror('invalid parameter: bar', 18, 0) 

    print 'validation succeeded' 
    -- do some work 
end 

Dies tat nicht den Trick seit Schwere 18 nicht die Ausführung stoppen und ‚Überprüfung erfolgreich‘ gedruckt wird, zusammen mit den Fehlermeldungen.

Ich weiß, dass ich einfach eine Rückkehr nach jedem raiserror hinzufügen könnte, aber das sieht irgendwie hässlich zu mir:

if (@fooInt = 0 and (@fooString is null or @fooString = '')) 
    begin 
    raiserror('invalid parameter: foo', 18, 0) 
    return 
    end 

    ... 

    print 'validation succeeded' 
    -- do some work 

Da Fehler mit Schweregrad 11 und höher innerhalb eines try/catch-Block einen anderen Ansatz gefangen ich getestet war, meine Fehlerprüfung innerhalb eines solchen try/catch-Blocks einzukapseln. Das Problem war, dass der Fehler verschluckt und nicht an den Client gesendet wurde. So habe ich einige der Forschung und fand einen Weg, um rethrow den Fehler:

ich mit diesem Ansatz immer noch nicht zufrieden bin so frag ich dich:

Wie sieht Ihre Parametervalidierung aus? Gibt es eine Art "Best Practice" für diese Art der Überprüfung?

Antwort

37

Ich denke nicht, dass es einen einzigen "richtigen" Weg gibt, dies zu tun.

Meine eigene Präferenz wäre ähnlich wie in Ihrem zweiten Beispiel, aber mit einem separaten Validierungsschritt für jeden Parameter und expliziteren Fehlermeldungen.

Wie Sie sagen, es ist ein wenig umständlich und hässlich, aber die Absicht des Codes ist offensichtlich für jeden, der es liest, und es macht die Arbeit erledigt.

IF (ISNULL(@fooInt, 0) = 0) 
BEGIN 
    RAISERROR('Invalid parameter: @fooInt cannot be NULL or zero', 18, 0) 
    RETURN 
END 

IF (ISNULL(@fooString, '') = '') 
BEGIN 
    RAISERROR('Invalid parameter: @fooString cannot be NULL or empty', 18, 0) 
    RETURN 
END 
+0

Gibt es einen Grund, warum Sie IF (ISNULL (@fooString, '') = '') statt IF (@fooString ist Null) verwendet? – macleojw

+9

@ makleojw: Er sucht nach Null und '' gleichzeitig .. clever :) – VVS

+6

Der zweite Validator hat ungültige Syntax: 'RAISEERROR'. Es sollte nur ein "e" geben. Lustig ist, dass es im Englischen korrekt ist, da 'raise + error' doppelt 'e' hat, aber nicht in MS SQL. –

1

Wir normalerweise vermeiden Raiseerror() und einen Wert zurückgeben, der eine negative Zahl einen Fehler, beispielsweise zeigt:

if <errorcondition> 
    return -1 

oder das Ergebnis bei zwei Parameter übergeben:

create procedure dbo.TestProc 
    .... 
    @result int output, 
    @errormessage varchar(256) output 
as 
set @result = -99 
set @errormessage = null 
.... 
if <errorcondition> 
    begin 
    set @result = -1 
    set @errormessage = 'Condition failed' 
    return @result 
    end 
+0

Warum bevorzugen Sie die Rückgabe über RaiseFehler()? – macleojw

+0

Raiseerror ist unvorhersehbar (kann Ausführung fortsetzen!) Und nicht Everyyr Client behandelt es auf die gleiche Weise. Ein Perl-Client könnte sterben! – Andomar

0

Ich ziehe es vor, so schnell wie möglich zurückzukehren, und sehe nicht darauf, dass alles am Ende des Verfahrens vom selben Punkt zurückkehrt. Ich habe diese Gewohnheit schon vor Jahren bei der Montage aufgenommen. Auch kehre ich immer einen Wert:

RETURN 10 

Die Anwendung wird einen fatalen Fehler auf positive Zahlen angezeigt werden, und die Benutzer Warnmeldung auf negative Werte angezeigt werden soll.

Wir geben immer einen OUTPUT-Parameter mit dem Text der Fehlermeldung zurück.

Beispiel:

IF ~error~ 
BEGIN 
    --if it is possible to be within a transaction, so any error logging is not ROLLBACK later 
    IF XACT_STATE()!=0 
    BEGIN 
     ROLLBACK 
    END 

    SET @OutputErrMsg='your message here!!' 
    INSERT INTO ErrorLog (....) VALUES (.... @OutputErrMsg) 
    RETURN 10 

END 
+0

Ich möchte den Sproc in einem anderen Sproc verwenden und möchte so wenig Fehler wie möglich zu tun, so scheint ein Fehler zu verursachen und es in der äußeren Sproc fangen scheint der beste Weg, um es zu tun. – VVS

+1

Eines Tages, wenn diese Prozedur von einem anderen Ort aufgerufen wird, hoffe ich, dass sie sich daran erinnern, die Fehler auch zu erfassen. Ich denke, es ist am besten, alle Fehler, wenn sie passieren, zu erfassen, lokal mit ihnen umzugehen und entsprechende Informationen zurückzugeben. –

1

Wie Sie aus dieser Antwort Geschichte sehen folgte ich diese Frage und Antwort akzeptiert, und fuhr dann fort zu ‚erfinden‘ eine Lösung, die im Grunde die gleiche wie Ihre zweite Ansatz war.

Koffein ist meine Hauptquelle für Energie, aufgrund der Tatsache, dass ich die meiste Zeit meines Lebens im Halbschlaf verbringe, da ich viel zu viel Zeit mit der Codierung verbringe; So habe ich meinen Faux-pas erst bemerkt, als du es zu Recht darauf hingewiesen hast.

Daher bevorzuge ich Ihren zweiten Ansatz: Verwenden Sie einen SP, um den aktuellen Fehler zu erhöhen, und verwenden Sie dann einen TRY/CATCH um Ihre Parameterüberprüfung.

Es reduziert die Notwendigkeit für alle IF/BEGIN/END-Blöcke und reduziert daher die Anzahl der Zeilen und legt den Fokus wieder auf die Validierung. Beim Lesen des Codes für den SP ist es wichtig, die Tests zu sehen, die an den Parametern durchgeführt werden; der ganze zusätzliche syntaktische Fluff, um den SQL-Parser zu erfüllen, steht meiner Meinung nach nur im Weg.

+0

Gibt es einen Grund, warum du meinen zweiten Ansatz kopierst und sogar den ursprünglich verwendeten Rethrow-SP neu erfunden hast? – VVS

+0

@VVS - oh du liegst richtig! Es war nicht absichtlich, ich las die Frage durch und durch die Antworten. Dann, wegen des Mangels an Schlaf, vergaß ich sofort die ganze Skala der Optionen, die hier gezeigt wurden, und ging anfänglich für das if/begin/end-Ergebnis, dann "entdeckte" diesen Ansatz - vergessend, dass dies eine der Möglichkeiten ist, die du ausprobiert hast. Entschuldigung! –

0

Ich verwende immer Parameter @Is_Success Bit als OUTPUT. Wenn ich also einen Fehler habe, dann ist @ Is_success = 0. Wenn die übergeordnete Prozedur das @ Is_Success = 0 überprüft, setzt sie ihre Transaktion zurück (mit untergeordneten Transaktionen) und sendet eine Fehlermeldung von @Error_Message an den Client.

+1

Hm, das macht nur Sinn, wenn der Rückgabewert des SP sonst verwendet wird. Oder gibt es einen guten Grund, warum nicht einfach "RETURN x"? – VVS

+0

Sie haben immer zwei Möglichkeiten für Fehler: SQL-Fehler (zB XML-Parsing) und logisch (zB Constraint-Fehler). Wenn Sie keinen Unterschied machen, verlieren Sie die Kontrolle über das Anwendungsverhalten. Wenn Sie Fehler außerhalb von SQL protokollieren, zB im Dateisystem, sollte Ihre Anwendung einen Fehlertyp erhalten. – Dalex