Как прервать выполнение SQL-скрипта

16

Я работаю над сценарием SQL, и у меня есть требование прекратить продолжение сценария, если некоторые условия не выполняются.

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

Можете ли вы предоставить мне, каковы возможные альтернативы, чтобы остановить выполнение сценария SQL.

Новый разработчик
источник
1
Почему поднятие ошибки недопустимо? Также этот сценарий является хранимой процедурой?
Namphibian
Я не совсем понял ваш первый вопрос. Для второго вопроса; нет, это не SP
новый разработчик
1
Какой сценарий? Он состоит из нескольких партий? Вы видели ответы здесь?
Мартин Смит

Ответы:

8

Из документации RAISERROR (выделено мое):

Уровни серьезности от 0 до 18 могут быть указаны любым пользователем. Уровни серьезности от 19 до 25 могут указываться только членами предопределенной роли сервера sysadmin или пользователями с разрешениями ALTER TRACE. Для уровней серьезности от 19 до 25 требуется опция WITH LOG.

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

Там нет ничего плохого в использовании RAISERROR; вы просто используете уровень серьезности, который является чрезмерным. Я использую уровень 16 по умолчанию для ошибки, которая возникает, и последовательность будет прервана. Если вы хотите быть более точным, вы можете следовать уровням, данным самой Microsoft:

введите описание изображения здесь

Теперь, сказав все это, в зависимости от контекста сценария, используя RAISERROR может быть недостаточным, так как он не «сам по себе» выходит из сценария (используя нормальные уровни серьезности).

Например:

RAISERROR(N'Test', 16, 1);

SELECT 1;   /* Executed! */

Это и вызовет ошибку и возвращает набор результатов.

Для немедленного завершения сценария я предпочитаю использовать RETURN(использование GOTOконструкций -type обычно не рекомендуется в большинстве кругов программирования, где существуют альтернативы):

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */

Или обработайте ошибку, используя TRY/CATCH, что вызовет переход к CATCHблоку, если уровень серьезности равен 11 или выше:

BEGIN TRY
    RAISERROR(N'Test', 16, 1);
    SELECT 1;   /* Not executed */
END TRY
BEGIN CATCH
    SELECT 2;   /* Executed */
END CATCH

BEGIN TRY
    RAISERROR(N'Test', 10, 1);
    SELECT 1;   /* Executed */
END TRY
BEGIN CATCH
    SELECT 2;   /* Not executed */
END CATCH

Отдельная проблема заключается в том, что если сценарий охватывает несколько пакетов, он RETURNбудет выходить только из пакета :

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

SELECT 2;   /* Executed! */

Чтобы это исправить, вы можете проверить @@ERRORв начале каждой партии:

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

IF (@@ERROR != 0)
    RETURN;

SELECT 2;   /* Not executed */

Изменить: как правильно указывает Мартин Смит в комментариях, это работает только для 2 партий. Чтобы расширить до 3 или более пакетов, вы можете каскадно создавать ошибки, например, так (примечание: GOTOметод не решает эту проблему, так как метка назначения должна быть определена в пакете):

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

IF (@@ERROR != 0)
BEGIN
    RAISERROR(N'Error already raised. See previous errors.', 16, 1);
    RETURN;
END

SELECT 2;   /* Not executed */
GO

IF (@@ERROR != 0)
BEGIN
    RAISERROR(N'Error already raised. See previous errors.', 16, 1);
    RETURN;
END

SELECT 3;   /* Not executed */

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

Джон Сигел
источник
Это последнее предложение не работает. Смотри Pasbin . Мне здесь нравится метод sqlcmd
Martin Smith,
6

Вы можете использовать это GOTOутверждение, чтобы пропустить, где вы хотите. Другими словами, вы сталкиваетесь с ошибкой или каким-либо другим условием, и у вас может быть метка внизу скрипта (т.е. TheEndOfTheScript:) и просто выдается goto TheEndOfTheScript;оператор.

Вот быстрый пример:

print 'here is the first statement...';

print 'here is the second statement...';

-- substitute whatever conditional flow determining factor
-- you'd like here. I have chosen a dummy statement that will
-- always return true
--
if (1 = 1)
    goto TheEndOfTheScript;

print 'here is the third statement...';

print 'here is the fourth statement...';


TheEndOfTheScript:
print 'here is the end of the script...';

Результат этого выполнения будет следующим:

here is the first statement...
here is the second statement...
here is the end of the script...

Как видите, команда GOTOпропустила печать третьего и четвертого операторов и переместилась прямо к метке ( TheEndOfTheScript).

Томас Стрингер
источник
7
Работает только при наличии одного пакета, прерывается, как только вы выполняете оператор GO.
Габриэль
0

Согласитесь с тем SET NOEXEC ON/OFF, однако в Stored Procs (содержащем один блок) я просто используюRETURN оператор.

Предостережения: если в файле скрипта есть несколько GOоператоров, они RETURNбудут выходить только из текущего блока и переходить к следующему блоку / пакету.

Примечание: GOTOпредполагается, что это плохая практика кодирования, рекомендуется использовать " TRY..CATCH", так как он был введен с SQL Server 2008, а затем THROWв 2012 году.

Эдди Кумар
источник