Вопрос по sql, sql-server – «Правильный» способ проверки параметров хранимой процедуры

29

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

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

<code>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
</code>

Это не помогло, поскольку уровень серьезности 18 не останавливает выполнение и «проверка прошла успешно». печатается вместе с сообщениями об ошибках.

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

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

  ...

  print 'validation succeeded'
  -- do some work
</code>

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

<code>  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
</code>

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

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

Ваш Ответ

5   ответов
1

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

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

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

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

Есть ли причина, по которой вы копируете мой второй подход и даже заново изобретаете rethrow-SP, который я использовал изначально? VVS
@VVS - о, дорогой, ты прав! Это не было преднамеренным, я прочитал вопрос и ответы. Затем из-за недостатка сна я быстро забыл всю гамму опций, отображаемых здесь, и сначала пошел на результат if / begin / end, затем «обнаружили». этот подход - забывая, что это один из вариантов, которые вы пробовали. Извиняюсь!
0

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

Хм, это имеет смысл, только если возвращаемое значение SP используется иначе. Или есть веская причина, почему бы просто не сделать «ВОЗВРАТ x»? VVS
У вас всегда есть две возможности для ошибок: ошибка SQL (например, синтаксический анализ XML) и логическая (например, ошибка ограничения). Если между ними нет разницы, вы теряете контроль над поведением приложения. Если вы регистрируете ошибки за пределами SQL, например, в файловой системе, ваше приложение должно получить тип ошибки.
0

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

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
Я хочу использовать sproc в другом sproc и хочу сделать как можно меньше, проверяя ошибки, так что повышение ошибки и перехват ее во внешнем sproc, кажется, лучший способ сделать это. VVS
Когда-нибудь, когда эта процедура вызывается из другого места, я надеюсь, что они не забудут и ошибки перехватить. Я думаю, что лучше всего отслеживать все ошибки по мере их возникновения, обрабатывать их локально и возвращать соответствующую информацию.
44

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

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

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
Второй валидатор имеет недопустимый синтаксис: «RAISEERROR». Должен быть только один 'e'. Забавно, что по-английски это правильно как «поднять + ошибка» имеет двойной "е"; но не на языке MS SQL.
@macleojw: он проверяет на нулевое значение и 'apos;' в то же время .. умный :) VVS
Есть ли какая-то причина, по которой вы использовали IF (ISNULL (@fooString, 'apos;) = "a";), а не IF (@fooString имеет значение null)?
1

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

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
Raiseerror непредсказуем (может продолжить выполнение!), И клиент eveyr не может работать с ним таким же образом. Клиент Perl может умереть!
Почему вы предпочитаете возвращение, а не рейзеррор ()?

Похожие вопросы