PL/SQL: есть ли инструкция полностью остановить выполнение script?

Я пытаюсь сделать некоторые проверки в схеме БД в начале PL/SQL script.

Если проверки дают неуспешные результаты, я хочу остановить script, чтобы предотвратить выполнение следующих команд.

У меня есть что-то вроде этого

-- 1st line of PL/SQL script

DECLARE
  SOME_COUNT INTEGER;
BEGIN
  SELECT COUNT(*) INTO SOME_COUNT FROM SOME_TABLE WHERE <SOME_CONDITIONS>;
  IF (SOME_COUNT > 0) THEN
    DBMS_OUTPUT.PUT_LINE('Test failed, I don''want the rest of the script'
      || ' to be executed.');
    --EXIT or something like that?... <= STOP EXECUTION HERE
  END IF;
END;
/

-- OTHER SQL INSTRUCTIONS...
ALTER TABLE SOME_TABLE ...

Я ищу инструкцию (ы), которая позволяет делать "STOP EXECUTION HERE".

Ответ 1

Исходя из вопроса, я не согласен с принятым ответом. В вопросе отображается пакет с несколькими сообщениями script. RAISE_APPLICATION_ERROR() только выходит из блока PL/SQL (подпрограммы), а не из общего script (как указано в Justin), поэтому он будет продолжен с последующими утверждениями.

Для пакетных скриптов лучше использовать WHENEVER SQLERROR EXIT. Да, это директива SQL * Plus, а не стандартный SQL, но довольно портативна; самые популярные инструменты Oracle, которые поддерживают скрипты, поддерживают эту директиву, по крайней мере частично. Следующий пример работает в SQL * Plus, SQL * Developer, Toad, SQLsmith и, возможно, других, и демонстрирует проблему, если вы прокомментируете строку.

set serveroutput on

-- Without this line, things keep going
WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK;

BEGIN
  IF (1 > 0) THEN
    DBMS_OUTPUT.PUT_LINE('First thing');
    RAISE_APPLICATION_ERROR(-20000, 'Test failed'); -- not enough
  END IF;
END;
/

-- This will execute if you remove WHEN SQLERROR.., so RAISE_APPLICATION_ERROR is not enough
BEGIN
   DBMS_OUTPUT.PUT_LINE('Second thing - Executes anyway');
END;
/

Если вы удалите WHEN SQLERROR, script продолжит и выполнит второй блок и т.д., что именно то, что задает вопрос.

Преимущество в этом случае графических инструментов, которые эмулируют sqlplus, заключается в том, что они действительно останавливают script и не отправляют оставшуюся часть script в командную оболочку в виде команд оболочки, что и происходит если вы вставляете скрипты в SQL * Plus, работающие в окне консоли. SQL * Plus может выйти из строя, но оставшиеся буферизованные команды будут обрабатываться оболочкой ОС, которая является немного беспорядочной и потенциально рискованной, если в комментариях есть команды оболочки (что неслыханно). С SQLPlus всегда лучше всего подключиться, а затем выполнить script или передать его в <start> аргумент командной строки (sqlplus scott/tiger @foo.sql), чтобы избежать этого.

Ответ 2

Если вы не хотите поднимать исключение, вы можете попробовать что-то вроде (untested):

declare
  SOME_COUNT INTEGER;
begin
  SELECT COUNT(*) INTO SOME_COUNT FROM SOME_TABLE WHERE <SOME_CONDITIONS>;
  IF (SOME_COUNT > 0) THEN
    DBMS_OUTPUT.PUT_LINE('Test failed, I don''want the rest of the script'
      || ' to be executed.');

    goto end_proc;
  END IF;

  -- A bunch of great code here

  <<end_proc>>
  null;  -- this could be a commit or other lines of code
end;

Некоторые люди ненавидят какие-либо операторы GOTO, поскольку они могут приводить к коду спагетти, если они злоупотребляют, но в простых ситуациях, подобных этому (опять же, если вы не хотите создавать исключение), они хорошо работают imo.

Ответ 3

Несколько секунд поискового запроса дали мне ответ: функция RAISE_APPLICATION_ERROR()

  IF (SOME_COUNT > 0) THEN
    RAISE_APPLICATION_ERROR(-20000, 'Test failed');
  END IF;

Пользовательский код ошибки должен быть между -20000 и -20999.

Подробнее о документе Oracle здесь: http://docs.oracle.com/cd/B10501_01/appdev.920/a96624/07_errs.htm#877 (раздел Определение собственных сообщений об ошибках: процедура RAISE_APPLICATION_ERROR)

Ответ 4

Вместо того, чтобы бросать ошибку приложения, гораздо проще просто использовать RETURN keyword, который выходит из текущего блока PL/SQL очень плавно.

Просто убедитесь, что вы делаете DBMS_OUTPUT.PUT_LINE('Exited because <error') перед этим, чтобы предоставить пользователю приятное сообщение о том, почему вы, конечно, выходите!