2009-06-29 14 views
25

Mam procedurę składowaną, która dokonuje weryfikacji niektórych parametrów i powinna zakończyć się niepowodzeniem i zakończyć wykonywanie, jeśli parametr jest niepoprawny."Prawidłowy" sposób sprawdzania poprawności procedury przechowywanej

Moje pierwsze podejście do sprawdzania błędów wyglądał następująco:

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 

To nie załatwi od nasilenia 18 nie zatrzymuje wykonanie i „walidacji udało” jest drukowany wraz z komunikatami o błędach.

wiem, może po prostu dodać powrót po każdym RAISERROR ale wygląda to rodzaj brzydki do mnie:

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

    ... 

    print 'validation succeeded' 
    -- do some work 

Od błędów z powagi 11 i wyższe są wychwytywane wewnątrz bloku try/catch innego podejścia Testowałem było zamknięcie mojego sprawdzania błędów w takim bloku try/catch. Problem polegał na tym, że błąd został połknięty i w ogóle nie wysłany do klienta. Więc zrobiłem kilka badań i znaleźć sposób, aby rethrow błąd:

ja nadal nie jestem zadowolony z tego podejścia, więc pytam Cię:

jaki sposób Państwa walidacja parametrów wyglądają? Czy istnieje jakaś "najlepsza praktyka" do tego rodzaju sprawdzania?

Odpowiedz

37

Nie sądzę, że istnieje jeden "właściwy" sposób, aby to zrobić.

Moja własna preferencja byłaby podobna do twojego drugiego przykładu, ale z oddzielnym krokiem weryfikacji dla każdego parametru i bardziej jawnymi komunikatami o błędach.

Tak jak mówisz, jest to trochę kłopotliwe i brzydkie, ale intencja kodu jest oczywista dla każdego, kto go czyta i wykonuje swoją pracę.

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

Czy istnieje jakiś powód, dla którego użyłeś IF (ISNULL (@fooString, '') = '') zamiast IF (@fooString ma wartość null)? – macleojw

+9

@macleojw: Sprawdza null i "'w tym samym czasie .. sprytny :) – VVS

+6

Drugi weryfikator ma nieprawidłową składnię:" RAISEERROR ". Powinno być tylko jedno "e". Zabawne jest to, że po angielsku jest to poprawne, ponieważ "podniesienie + błąd" ma podwójne "e", ale nie w języku MS SQL. –

1

Zwykle uniknąć raiseError() i zwraca wartość wskazującą błąd, na przykład liczbę ujemną:

if <errorcondition> 
    return -1 

albo przekazać wynik w dwóch z parametrami:

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

Dlaczego wolisz return over uperror()? – macleojw

+0

Raiseerror jest nieprzewidywalny (może kontynuować wykonanie!), A klient nie radzi sobie z nim w taki sam sposób. Klient perl może umrzeć! – Andomar

0

Wolę jak najszybciej odejść i nie wskazywać na to, że wszystko wyleci z tego samego punktu na końcu procedury. Podniosłem ten nawyk, robiąc zebranie, lata temu. Ponadto, zawsze zwraca wartość:

RETURN 10 

aplikacja wyświetla błąd krytyczny na liczbach dodatnich i wyświetli komunikat z ostrzeżeniem użytkownika na wartości ujemne.

Zawsze przekazujemy parametr OUTPUT z tekstem komunikatu o błędzie.

przykład:

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

Chcę użyć sproc w innym sprocu i chcę zrobić jak najmniejsze sprawdzanie błędów, tak więc podniesienie błędu i wyłapanie go w zewnętrznym sprocu wydaje się być najlepszym sposobem na zrobienie tego. – VVS

+1

Któregoś dnia, kiedy ta procedura jest wywoływana z innej lokalizacji, mam nadzieję, że pamiętają też, aby złapać błędy. Myślę, że najlepiej jest uchwycić wszystkie błędy, gdy się pojawią, zająć się nimi lokalnie i zwrócić odpowiednie informacje. –

1

Jak widać z tej historii Odpowiedź następuje to pytanie i zaakceptowane odpowiedź, a następnie przystąpił do „wynaleźć” rozwiązanie, które było w zasadzie takie samo jak drugie podejście.

Kofeina jest moim głównym źródłem energii, ze względu na fakt, że spędzam większość mojego życia w półśnie, ponieważ spędzam zbyt wiele czasu na kodowanie; w ten sposób nie zdawałem sobie sprawy z mojego faux-pasu, dopóki właściwie tego nie wskazałeś.

Dlatego, dla rekordu, preferuję twoje drugie podejście: użycie SP do podniesienia bieżącego błędu, a następnie użycie TRY/CATCH wokół sprawdzania poprawności parametrów.

Zmniejsza zapotrzebowanie na wszystkie bloki IF/BEGIN/END, a tym samym zmniejsza liczbę linii oraz przywraca fokus na walidacji. Podczas czytania kodu dla SP ważne jest, aby móc zobaczyć testy wykonywane na parametrach; wszystkie dodatkowe fluktuacje składniowe w celu zaspokojenia parsera SQL po prostu stają na drodze, w mojej opinii.

+0

Czy jest jakiś powód, dla którego kopiujesz moje drugie podejście, a nawet wymyśliłem ponowne wyrzucenie - SP, którego użyłem pierwotnie? – VVS

+0

@VVS - oh kochanie masz rację! To nie było celowe, przeczytałem pytanie i odpowiedzi. Następnie, z powodu braku snu, natychmiast zapomniałem o całej gamie wyświetlanych tutaj opcji i początkowo poszedłem do wyniku/rozpoczęcia/zakończenia, a następnie "odkryłem" to podejście - zapominając, że jest to jedna z opcji, które wypróbowałeś. Przeprosiny! –

0

Zawsze używam parametru @Is_Success bit jako OUTPUT. Więc jeśli mam błąd, to @ Is_success = 0. Gdy procedura nadrzędna sprawdza, czy @ Is_Success = 0, wówczas wycofuje swoją transakcję (z transakcjami podrzędnymi) i wysyła komunikat o błędzie z @Error_Message do klienta.

+1

Hm, który ma sens tylko wtedy, gdy wartość zwracana przez SP jest używana w inny sposób. Czy istnieje dobry powód, dla którego nie wystarczy zrobić "POWRÓT x"? – VVS

+0

Zawsze masz dwie możliwości błędów: błąd SQL (np. Parsowanie XML) i logiczne (np. Błąd ograniczenia). Jeśli nie zmienisz między nimi, stracisz kontrolę nad zachowaniem aplikacji. Jeśli logujesz błędy poza SQL, np. W systemie plików, to twoja aplikacja powinna otrzymać typ błędu. – Dalex

Powiązane problemy