Подтвердить что ты не робот

"Правильный" способ проверки параметров хранимой процедуры

У меня есть хранимая процедура, которая выполняет некоторую проверку параметров и должна завершиться с ошибкой и прекратить выполнение, если параметр недействителен.

Мой первый подход к проверке ошибок выглядел так:

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

Это не делает трюк, так как строгость 18 не останавливает выполнение, а "проверка правильности" печатается вместе с сообщениями об ошибках.

Я знаю, что могу просто добавить возврат после каждого raiserror, но это выглядит как-то уродливо для меня:

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

  ...

  print 'validation succeeded'
  -- do some work

Так как ошибки с серьезностью 11 и выше попадают в блок try/catch, другой подход, который я тестировал, заключался в инкапсуляции моей проверки ошибок внутри такого блока try/catch. Проблема заключалась в том, что ошибка была проглочена и вообще не отправлена ​​клиенту. Поэтому я провел некоторое исследование и нашел способ rethrow ошибку:

  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

Я все еще не доволен этим подходом, поэтому я спрашиваю вас:

Как выглядит ваша проверка параметров? Есть ли какая-то "лучшая практика" для такого рода проверки?

4b9b3361

Ответ 1

Я не думаю, что есть один "правильный" способ сделать это.

Мои предпочтения будут похожи на ваш второй пример, но с отдельным шагом проверки для каждого параметра и более явных сообщений об ошибках.

Как вы говорите, это немного громоздко и уродливо, но цель кода очевидна для всех, кто его читает, и он выполняет свою работу.

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

Ответ 2

Обычно мы избегаем raiseerror() и возвращаем значение, указывающее на ошибку, например отрицательное число:

if <errorcondition>
    return -1

Или передайте результат по двум параметрам:

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

Ответ 3

Как вы можете видеть из этой истории ответов, я последовал этому вопросу и принял ответ, а затем приступил к "придумыванию" решения, которое в основном было таким же, как ваш второй подход.

Кофеин - мой главный источник энергии, из-за того, что большую часть жизни я провожу в полусмерти, поскольку я трачу слишком много времени на кодирование; таким образом, я не понимал, как я ошибаюсь, пока вы не указали правильно.

Поэтому для записи я предпочитаю ваш второй подход: используя SP для повышения текущей ошибки, а затем используя TRY/CATCH вокруг проверки параметров.

Это уменьшает необходимость во всех блоках IF/BEGIN/END и, следовательно, уменьшает количество строк, а также возвращает фокус на проверку. При чтении кода для SP важно иметь возможность видеть, какие тесты выполняются по параметрам; все лишние синтаксические пушицы, чтобы удовлетворить SQL-парсер просто мешает, на мой взгляд.

Ответ 4

Я предпочитаю возвращаться как можно скорее, и не вижу, чтобы все возвращалось из той же точки в конце процедуры. Я взял эту привычку делать сборку несколько лет назад. Кроме того, я всегда возвращаю значение:

RETURN 10

Приложению будет отображаться фатальная ошибка в положительных числах и будет отображать предупреждающее сообщение пользователя об отрицательных значениях.

Мы всегда возвращаем параметр OUTPUT с текстом сообщения об ошибке.

Пример:

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

Ответ 5

Я всегда использую бит параметра @Is_Success как OUTPUT. Поэтому, если у меня есть ошибка, то @Is_success = 0. Когда родительская процедура проверяет, что @Is_Success = 0, она откатывает транзакцию (с дочерними транзакциями) и отправляет клиенту сообщение об ошибке из @Error_Message.