"Właściwy" sposób walidacji parametrów procedury składowanej

Mam procedurę składowaną, która wykonuje walidację parametrów i powinna zawieść i zatrzymać wykonanie, jeśli parametr nie jest poprawny.

Moje pierwsze podejście do sprawdzania błędów wyglądało tak:
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 zadziałało, ponieważ dotkliwość 18 nie zatrzymuje wykonania, a "Walidacja powiodła się" jest drukowana wraz z komunikatami o błędach.

Wiem, że mógłbym po prostu dodać zwrot po każdym raiserror, ale to wygląda trochę brzydko dla 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 dotkliwością 11 i wyższą są złowione w bloku try/catch innym podejściem, które testowałem, było Zamknięcie mojego błędu sprawdzania wewnątrz takiego bloku try / catch. Problem polegał na tym, że błąd został połknięty i w ogóle nie został wysłany do klienta. Więc poszperałem trochę i znalazłem sposób narethrow błąd:

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

    ...
  end try
  begin catch
    exec usp_RethrowError
    return
  end catch

  print 'validation succeeded'
  -- do some work

Nadal nie jestem zadowolony z tego podejścia więc pytam:

Jak wygląda Walidacja parametrów? Czy istnieje jakaś "najlepsza praktyka", aby robić tego rodzaju sprawdzania?

Author: Matthew Murdoch, 2009-06-29

5 answers

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

Moje własne preferencje byłyby podobne do twojego drugiego przykładu, ale z oddzielnym krokiem walidacji dla każdego parametru i bardziej wyraźnymi komunikatami o błędach.

Jak mówisz, jest to trochę kłopotliwe i brzydkie, ale intencja kodu jest oczywista dla każdego, kto go czyta, i robi robotę.

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
 41
Author: LukeH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-19 11:52:09

Zwykle unikamy funkcji raiseerror () i zwracamy wartość wskazującą na błąd, na przykład liczbę ujemną:

if <errorcondition>
    return -1

Lub przekazać wynik w dwóch wyjściowych parametrach:

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
 1
Author: Andomar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 12:20:33

Jak widać z historii odpowiedzi podążałem za tym pytaniem i zaakceptowałem odpowiedź, a następnie zacząłem "wymyślać" rozwiązanie, które w zasadzie było takie samo, jak twoje drugie podejście.

Kofeina jest moim głównym źródłem energii, ze względu na fakt, że większość mojego życia półśnie, ponieważ spędzam zbyt dużo czasu na kodowaniu; więc nie zdawałem sobie sprawy z mojego faux-pas, dopóki nie słusznie wskazał go.

Dlatego, dla przypomnienia, wolę twoje drugie podejście: użycie SP do podniesienia aktualny błąd, a następnie za pomocą TRY / CATCH wokół walidacji parametrów.

Zmniejsza zapotrzebowanie na wszystkie bloki IF / BEGIN/END, a tym samym zmniejsza liczbę linii, a także kładzie nacisk na walidację. Podczas czytania kodu dla SP ważne jest, aby być w stanie zobaczyć testy wykonywane na parametrach; wszystkie dodatkowe fluff składniowy, aby zaspokoić parser SQL po prostu dostaje w drodze, moim zdaniem.

 1
Author: Andras Zoltan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-26 08:57:53

Wolę wrócić jak najszybciej, i nie widzę sensu, aby wszystko wróciło z tego samego punktu na końcu procedury. Przywykłem do tego, robiąc zbiórki, lata temu. Ponadto zawsze zwracam wartość:

RETURN 10

Aplikacja wyświetli błąd krytyczny na liczbach dodatnich i wyświetli komunikat ostrzegawczy użytkownika na wartościach ujemnych.

Zawsze przekazujemy parametr wyjściowy 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
Author: KM.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-29 12:32:17

Zawsze używam parametru @ Is_Success bit jako wyjścia. Więc jeśli mam błąd to @Is_success=0. Gdy procedura rodzica sprawdzi, że @Is_Success=0, to wycofuje swoją transakcję (z transakcjami potomnymi) i wysyła komunikat o błędzie z @Error_Message do klienta.

 0
Author: Dalex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-20 10:53:07