ГЛАВА 3

        ----------------------------------------------------------------

        УПРАВЛЯЮЩИЕ СТРУКТУРЫ


                            One ship drives east and another drives west
                                      With the selfsame winds that blow.
                             'Tis the set of the sails and not the gales
                                           Which tells us the way to go.
                                                     Ella Wheeler Wilcox

                                  (Кто едет на запад, а кто - на восток,
                                                 А ветер - один на всех.
                                         Не ветер, а парус, ткани кусок,
                                              Диктует твой путь и успех.
                                                     Элла Уилер Уилкокс)

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





































                                              Управляющие структуры  3-1


----------------

Обзор

        Согласно  ТЕОРЕМЕ  О  СТРУКТУРАХ,  любая  компьютерная программа
        может быть написана  с использованием трех  основных управляющих
        структур,   показанных   на   рис.3-1.    Эти   структуры  можно
        комбинировать  любым  способом,  необходимым  для решения данной
        проблемы.

Рис.3-1
Управляющие структуры

          ВЫБОР                      ИТЕРАЦИЯ        ПОСЛЕДОВАТЕЛЬНОСТЬ

            ¦                           ¦                   ¦
                                                          
      T г---+---¬ F                 г---+---¬ F         ----+---¬
    ----¦       ¦---¬           ---¦       ¦---¬       ¦       ¦
    ¦   L--------   ¦           ¦   L---T----   ¦       L---T----
                              ¦        T     ¦           
----+---¬       ----+---¬       ¦   ----+---¬   ¦       ----+---¬
¦       ¦       ¦       ¦          ¦       ¦   ¦       ¦       ¦
L---T----       L---T----       ¦   L---T----   ¦       L---T----
    ¦      --¬      ¦           ¦              ¦           
    L-----+ +------           L--------              ----+---¬
           LT-                                          ¦       ¦
                                                       L---T----
                                                            

        Структура  выбора  проверяет  условие  и  в  зависимости  от его
        истинности   выбирает   одну   или   другую   последовательность
        предложений.   УСЛОВИЕ  -  это  любая  переменная или выражение,
        возвращающее  булевское   значение  (TRUE,   FALSE  или   NULL).
        Структура   итерации   повторно   выполняет   последовательность
        предложений,   пока   условие   остается   истинным.   Структура
        последовательности    просто    выполняет     последовательность
        предложений в том порядке, в котором они встречаются.

        Теперь взглянем на эти структуры ближе.

----------------

Условное управление: предложения IF

        Часто бывает  необходимо предпринять  альтернативные действия  в
        зависимости  от  обстоятельств.   Предложение  IF  позволяет вам
        выполнить последовательность  предложений условно.   Это значит,
        что, будет выполнена эта последовательность или нет, зависит  от
        значения  условия.   Есть  три  формы  предложений  IF: IF-THEN,
        IF-THEN-ELSE и IF-THEN-ELSIF.

IF-THEN
-------

        Простейшая   форма   предложения   IF   ассоциирует   условие  с
        последовательностью  предложений,  окружаемой  ключевыми словами
        THEN и END IF (не ENDIF), как показано ниже:

        IF условие THEN
            ряд_предложений;
        END IF;

3-2  Руководство пользователя и справочник по PL/SQL


        Последовательность предложений выполняется, только если  условие
        дает TRUE.  Если условие дает FALSE или NULL, то предложение  IF
        ничего  не  делает.   В  любом  случае, управление передается на
        следующее предложение.  Пример:

        IF sales > quota THEN
            compute_bonus(emp_id);
            UPDATE payroll SET pay = pay + bonus WHERE empno = emp_id;
        END IF;

        Вы можете,  если хотите,  записывать короткие  предложения IF  в
        одну строку, например:

        IF x > y THEN high := x; END IF;

IF-THEN-ELSE
------------

        Вторая форма  предложения IF  добавляет ключевое  слово ELSE, за
        которым следует альтернативная последовательность предложений:

        IF условие THEN
            ряд_предложений1;
        ELSE
            ряд_предложений2;
        END IF;

        Последовательность предложений в фразе ELSE выполняется,  только
        если условие  дает FALSE  или NULL.   Таким образом,  фраза ELSE
        гарантирует, что одна  из последовательностей предложений  будет
        выполнена.  В следующем  примере, первое или  второе предложение
        UPDATE будет выполнено, когда условие соответственно истинно или
        ложно:

        IF trans_type = 'CR' THEN
            UPDATE accounts SET balance = balance + credit WHERE ...
        ELSE
            UPDATE accounts SET balance = balance - debit WHERE ...
        END IF;

        Последовательности предложений,  включаемые в  фразы IF  и ELSE,
        сами могут содержать предложения IF. Следовательно,  предложения
        IF могут  вкладываться друг  в друга,  как показывает  следующий
        пример:

        IF trans_type = 'CR' THEN
            UPDATE accounts SET balance = balance + credit WHERE ...
        ELSE
            IF new_balance >= minimum_balance THEN
                UPDATE accounts SET balance = balance - debit WHERE ...
            ELSE
                RAISE insufficient_funds;
            END IF;
        END IF;









                                              Управляющие структуры  3-3


IF-THEN-ELSIF
-------------

        Иногда  вы  хотите   выбрать  действие  из   нескольких  взаимно
        исключающих альтернатив.  Третья форма предложения IF использует
        ключевое слово ELSIF (не  ELSE IF), чтобы ввести  дополнительные
        условия:

        IF условие1 THEN
            ряд_предложений1;
        ELSIF условие2 THEN
            ряд_предложений2;
        ELSE
            ряд_предложений3;
        END IF;

        Если первое условие дает  FALSE или NULL, фраза  ELSIF проверяет
        следующее условие.  В предложении  IF может быть сколько  угодно
        фраз  ELSIF;  последняя   фраза  ELSE  необязательна.    Условия
        вычисляются  по  одному  сверху  вниз.   Если любое условие даст
        TRUE,     выполняется     соответствующая     последовательность
        предложений,  и  управление   передается  на  следующее   за  IF
        предложение  (без  вычисления  оставшихся  условий).   Если  все
        условия  дадут  FALSE  или  NULL, выполняется последовательность
        предложений в фразе ELSE,  если она есть.  Рассмотрим  следующий
        пример:

        IF sales > 50000 THEN
            bonus := 1500;
        ELSIF sales > 35000 THEN
            bonus := 500;
        ELSE
            bonus := 100;
        END IF;
        INSERT INTO payroll VALUES (emp_id, bonus, ...);

        Если значение sales превышает  50000, истинны как первое,  так и
        второе условия.   Тем не  менее, переменной  bonus присваивается
        правильное значение 1500, потому что второе условие  проверяться
        не  будет,  а  управление  сразу  будет  передано на предложение
        INSERT.






















3-4  Руководство пользователя и справочник по PL/SQL


Советы
------

        Избегайте неуклюжих предложений IF, подобных следующему примеру:

        DECLARE
            ...
            overdrawn  BOOLEAN;
        BEGIN
            ...
            IF new_balance < minimum_balance THEN
                overdrawn := TRUE;
            ELSE
                overdrawn := FALSE;
            END IF;
            ...
            IF overdrawn = TRUE THEN
                RAISE insufficient_funds;
            END IF;
        END;

        Этот  код  игнорирует  два  полезных факта.  Во-первых, значение
        булевского выражения можно непосредственно присваивать булевской
        переменной.  Так, первое  предложение IF можно  заменить простым
        присваиванием:

        overdrawn := new_balance < minimum_balance;

        Во-вторых, булевская  переменная сама  имеет значение  TRUE либо
        FALSE.   Поэтому   условие  во   втором  предложении   IF  можно
        упростить:

        IF overdrawn THEN ...

        По мере  возможности, используйте  фразу ELSIF  вместо вложенных
        предложений IF. При этом ваш код будет легче читать и  понимать.
        Сравните следующие предложения IF:

        IF условие1 THEN              |        IF условие1 THEN
            предложение1;             |            предложение1;
        ELSE                          |        ELSIF условие 2 THEN
            IF условие2 THEN          |            предложение2;
                предложение2;         |        ELSIF условие3 THEN
            ELSE                      |            предложение3;
                IF условие3 THEN      |        END IF;
                    предложение3;     |
                END IF;               |
            END IF;                   |
        END IF;                       |


        Эти  предложения  логически  эквивалентны,  но  первое  из   них
        затемняет логику, тогда как второе проявляет ее.










                                              Управляющие структуры  3-5


----------------

Итеративное управление: Предложения LOOP и EXIT

        Предложения   LOOP   позволяют   выполнить    последовательность
        предложений  несколько  раз.   Есть  три формы предложения LOOP:
        LOOP, WHILE-LOOP и FOR-LOOP.

LOOP
----

        Простейшую  форму  предложения  LOOP  представляет основной (или
        бесконечный)   цикл,    который   окружает    последовательность
        предложений между ключевыми словами LOOP и END LOOP:

        LOOP
            ряд_предложений
        END LOOP;

        При   каждой   итерации   цикла   последовательность предложений
        выполняется,  а  затем  управление  передается  на начало цикла.
        Если  дальнейшее  повторение  нежелательно  или  невозможно,  вы
        можете использовать предложение EXIT, чтобы закончить цикл.   Вы
        можете поместить сколько  угодно предложений EXIT  внутри цикла,
        но только не вне цикла.  Есть две формы предложения EXIT: EXIT и
        EXIT WHEN.

EXIT

        Предложение EXIT форсирует безусловное завершение цикла.   Когда
        встречается предложение EXIT,  цикл немедленно заканчивается,  и
        управление передается  на следующее  (за END  LOOP) предложение.
        Пример:

        LOOP
            ...
            IF ... THEN
                ...
                EXIT;  -- немедленно выходит из цикла
            END IF;
        END LOOP;
        -- управление передается сюда

        Как  показывает  следующий  пример,  вы  не  можете использовать
        предложение EXIT, чтобы завершить блок PL/SQL:

        BEGIN
            ...
            IF ... THEN
                ...
                EXIT;  -- незаконно
            END IF;
        END;

        Не забывайте, что предложение EXIT можно применять только внутри
        цикла.   Чтобы   выйти  из   блока  PL/SQL   до  достижения  его
        нормального конца,  можно использовать  предложение RETURN  (см.
        главу 9).





3-6  Руководство пользователя и справочник по PL/SQL


EXIT-WHEN

        Предложение EXIT-WHEN позволяет  завершить цикл условно.   Когда
        встречается это предложение, вычисляется условие в фразе  WHERE.
        Если  это  условие  дает  TRUE,  цикл  завершается, и управление
        передается на предложение, следующее за циклом.  Пример:

        LOOP
            FETCH c1 INTO ...
            EXIT WHEN c1%NOTFOUND;  -- выйти из цикла при условии
            ...
        END LOOP;
        CLOSE c1;

        Пока  условие  не  станет  истинным,  цикл не может завершиться.
        Поэтому,  предложения  внутри  цикла  должны  изменять  значение
        условия.  В последнем примере, если предложение FETCH  извлекает
        строку, условие дает FALSE.   Когда предложение FETCH не  сможет
        возвратить  строку,  условие  даст  TRUE,  цикл  завершится,   и
        управление будет передано на предложение CLOSE.

        Предложение EXIT-WHEN заменяет простое предложение IF. Например,
        сравните следующие предложения:

        IF count > 100 THEN          |          EXIT WHEN count > 100;
            EXIT;                    |
        END IF;                      |

        Эти предложения логически эквивалентны, но предложение EXIT-WHEN
        легче читается и понимается.

Метки циклов

        Как  и   блоки  PL/SQL,   циклы  могут   иметь  метки.    Метка,
        необъявляемый идентификатор  в двойных  угловых скобках,  должна
        появиться в начале предложения LOOP:

        <<имя_метки>>
        LOOP
            ряд_предложений
        END LOOP;

        Имя метки  цикла может  также (необязательно)  появиться в конце
        цикла в предложении END LOOP, как показывает следующий пример:

        <>
        LOOP
            ...
        END LOOP my_loop;

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











                                              Управляющие структуры  3-7


        Обе формы предложения EXIT позволяют выйти не только из текущего
        цикла, но из любого окружающего цикла.  Просто дайте метку  тому
        циклу, который вы хотите завершить, а затем укажите эту метку  в
        предложении EXIT:

        <>
        LOOP
            ...
            LOOP
                ...
                EXIT outer WHEN ...  -- выйти из обоих циклов
            END LOOP;
            ...
        END LOOP outer;

        Выход осуществляется из всех окружающих циклов, вплоть до  того,
        чья метка специфицирована в предложении EXIT.


WHILE-LOOP
----------

        Предложение WHILE-LOOP ассоциирует условие с последовательностью
        предложений, окруженной ключевыми словами LOOP и END LOOP:

        WHEN условие LOOP
            ряд_предложений;
        END LOOP;

        Перед каждой итерацией цикла условие проверяется.  Если оно дает
        TRUE,   то   последовательность   предложений   выполняется,   и
        управление  возвращается  на  начало  цикла.   Если условие дает
        FALSE или NULL,  то цикл обходится,  и управление передается  на
        следующее предложение.  Пример:

        WHILE total <= 25000 LOOP
            ...
            SELECT sal INTO salary FROM emp WHERE ...
            total := total + salary;
        END LOOP;

        Число повторений цикла  зависит от условия  и неизвестно до  тех
        пор, пока цикл не  завершится.  Поскольку условие проверяется  в
        начале   цикла,   последовательность   предложений   может    не
        выполниться  ни  разу.   В  последнем  примере,  если  начальное
        значение переменной  total окажется  больше 25000,  условие даст
        FALSE, и цикл будет обойден.
















3-8  Руководство пользователя и справочник по PL/SQL


        В  некоторых  языках  имеется  структура  LOOP  UNTIL или REPEAT
        UNTIL, которая проверяет условие не в начале, а в конце итерации
        цикла.   Поэтому  гарантируется  хотя  бы однократное выполнение
        тела цикла.  В PL/SQL такой структуры нет, но вы легко можете ее
        смоделировать:

        LOOP
            ряд_предложений;
            EXIT WHEN булевское_выражение;
        END LOOP;

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

        done := FALSE;
        WHILE NOT done LOOP
            ряд_предложений;
            done := булевское_выражение;
        END LOOP;

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

        WHILE TRUE LOOP           |             LOOP
            ...                   |                 ...
        END LOOP;                 |             END LOOP;


FOR-LOOP
--------

        В то время как число итераций цикла WHILE неизвестно до тех пор,
        пока цикл не завершится,  для цикла FOR число  итераций известно
        до того, как войти в цикл.  Циклы FOR осуществляют свои итерации
        по  заданному  интервалу  целых  чисел.   (Курсорные  циклы FOR,
        которые повторяются по активному множеству курсора,  обсуждаются
        в  главе  4.)   Этот  интервал  является  частью СХЕМЫ ИТЕРАЦИЙ,
        которая окружается ключевыми словами FOR и  LOOP.   Синтаксис
        имеет следующий вид:

        FOR счетчик IN [REVERSE] нижняя_граница..верхняя_граница LOOP
            ряд_предложений;
        END LOOP;

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

        FOR i IN 1..3 LOOP -- присваивает переменной i значения 1, 2, 3
            ряд_предложений;  -- будет выполнен три раза
        END LOOP;








                                              Управляющие структуры  3-9


        Как показывает следующий  пример, если нижняя  граница интервала
        совпадает с верхней, цикл выполняется один раз:

        FOR i IN 3..3 LOOP -- присваивает переменной i значение 3
            ряд_предложений;  -- будет выполнен один раз
        END LOOP;

        По  умолчанию  индекс  наращивается  на  1  от нижней до верхней
        границы.  Однако,  если вы  используете ключевое  слово REVERSE,
        индекс  будет  изменяться  в  обратном  направлении,  от верхней
        границы к нижней, как показывает следующий пример.  После каждой
        итерации индекс уменьшается на 1.

        FOR i IN REVERSE 1..3 LOOP -- присваивает переменной i 3, 2, 1
            ряд_предложений;  -- будет выполнен три раза
        END LOOP;

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

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

        FOR ctr IN 1..10 LOOP
            ...
            IF NOT finished THEN
                INSERT INTO ... VALUES (ctr, ...);  -- законно
                factor := ctr * 2;  -- законно
                ...
            ELSE
                ctr := 10;  -- незаконно
            END IF;
        END LOOP;




























3-10  Руководство пользователя и справочник по PL/SQL


Схемы итераций

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

        j IN -5..5
        k IN REVERSE first..last
        step IN 0..TRUNC(high/low) * 2
        code IN ASCII('A')..ASCII('J')

        Как  видите,  нижняя  граница  не  обязана быть равна 1.  Однако
        приращение (или отрицательное приращение) счетчика цикла  всегда
        равно 1.  В некоторых языках существует фраза, с помощью которой
        можно задать другую величину приращения.  Пример на языке BASIC:

        FOR J = 5 to 15 STEP 5  :REM присваивает J значения 5,10,15
            ряд_предложений  -- J имеет значения 5,10,15
        NEXT J

        В  PL/SQL  такой  структуры  не  существует,  но вы можете легко
        смоделировать ее.  Рассмотрим следующий пример:

        FOR j IN 5..15 LOOP  -- присваивает j значения 5,6,7,...
            IF MOD(j, 5) = 0 THEN  -- выбирает кратные 5
                ряд_предложений;  -- j имеет значения 5,10,15
            END IF;
        END LOOP;

        Этот  цикл  логически  эквивалентен  предыдущему  циклу   BASIC.
        Внутри поседовательности предложений  счетчик цикла будет  иметь
        лишь значения 5, 10 и 15.

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

        FOR j IN 1..3 LOOP  -- присваивает j значения 1,2,3
            ряд_предложений;  -- вместо j обращаться к j*5
        END LOOP;

Динамические интервалы

        PL/SQL позволяет определять интервал цикла динамически во  время
        выполнения, как показывает следующий пример:

        SELECT COUNT(empno) INTO emp_count FROM emp;
        FOR i IN 1..emp_count LOOP
            ...
        END LOOP;

        Значение emp_count неизвестно  во время компиляции;  предложение
        SELECT возвращает это значение во время выполнения.









                                             Управляющие структуры  3-11


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

        -- limit получает значение 1
        FOR i IN 2..limit LOOP
            ряд_предложений;  -- не выполнится ни разу
        END LOOP;
        -- управление будет передано сюда

Правила сферы

        Счетчик  цикла  определен  только  внутри  цикла.   Вы не можете
        обратиться к  нему вне  цикла.  После  выхода из  цикла значение
        счетчика цикла не определено, как показывает следующий пример:

        FOR ctr IN 1..10 LOOP
            ...
        END LOOP;
        sum := ctr - 1;  -- незаконно

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

        DECLARE
            ctr  INTEGER;
        BEGIN
            ...
            FOR ctr IN 1..25 LOOP
                ...
                IF ctr > 10 THEN ...  -- обращается к счетчику цикла
            END LOOP;
        END;

        Чтобы в этом примере обратиться к глобальной переменной ctr,  вы
        должны использовать метку и квалифицированную ссылку:

        <
> DECLARE ctr INTEGER; BEGIN ... FOR ctr IN 1..25 LOOP ... IF main.ctr > 10 THEN ... --обращается к глоб.переменной END LOOP; END main; 3-12 Руководство пользователя и справочник по PL/SQL Такие же правила сферы применимы к вложенным циклам FOR. Рассмотрим следующий пример. Оба счетчика циклов имеют одно и то же имя. Поэтому для того, чтобы обратиться из внутреннего цикла к счетчику внешнего цикла, вы должны использовать метку и квалифицированную ссылку: <> FOR step IN 1..25 LOOP FOR step IN 1..10 LOOP ... IF outer.step > 15 THEN ... END LOOP; END LOOP outer; Использование предложения EXIT Предложение EXIT позволяет завершить цикл FOR прежде времени. Например, следующий цикл нормально выполняется десять раз, но, как только предложение FETCH не сможет вернуть очередную строку, цикл будет завершен независимо от того, сколько раз он успел выполниться. FOT j IN 1..10 LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%NOTFOUND; ... END LOOP; Предложение EXIT позволяет выйти не только из текущего цикла FOR, но из любого окружающего цикла. Просто дайте метку тому циклу, который вы хотите завершить, а затем укажите эту метку в предложении EXIT: <> FOR i IN 1..5 LOOP ... FOR j IN 1..10 LOOP FETCH c1 INTO emp_rec; EXIT outer WHEN c1%NOTFOUND; -- выход из обоих циклов ... END LOOP; END LOOP outer; -- управление будет передано сюда Управляющие структуры 3-13 ---------------- Последовательное управление: предложения GOTO и NULL В отличие от предложений IF и LOOP, предложения GOTO и NULL не являются ключевыми в программировании на языке PL/SQL. Структура языка такова, что предложение GOTO требуется редко. Иногда его применение может быть оправдано некоторым упрощением логики. Предложение NULL может прояснить смысл условных предложений в программе и улучшить читабельность. Предложение GOTO ---------------- Предложение GOTO выполняет безусловный переход к указанной метке. Метка должна быть уникальной в своей сфере, и должна предшествовать выполнимому предложению или блоку PL/SQL. Предложение GOTO передает управление на помеченное предложение или блок. В следующем примере управление передается на выполнимое предложение, находящееся дальше в последовательности предложений: BEGIN ... GOTO insert_row; ... <> INSERT INTO emp VALUES ... END; В следующем примере управление передается на блок PL/SQL, расположенный выше в последовательности предложений: BEGIN ... <> BEGIN UPDATE emp SET ... ... END; ... GOTO update_row; ... END; 3-14 Руководство пользователя и справочник по PL/SQL В следующем примере метка <> незаконна, потому что она стоит не перед выполнимым предложением: DECLARE done BOOLEAN; BEGIN ... FOR i IN 1..50 LOOP IF done THEN GOTO end_loop; END IF; ... <> -- незаконно END LOOP; -- это не выполняемое предложение END; Чтобы исправить последний пример, просто добавьте за меткой предложение NULL: DECLARE done BOOLEAN; BEGIN ... FOR i IN 1..50 LOOP IF done THEN GOTO end_loop; END IF; ... <> NULL; -- выполняемое предложение END LOOP; END; Как показывает следующий пример, предложение GOTO может передать управление в окружающий блок из текущего блока: DECLARE my_ename CHAR(10); BEGIN ... <> SELECT ename INTO my_ename FROM emp WHERE ... ... BEGIN ... GOTO get_name; -- переход в окружающий блок END; END; Предложение GOTO передает управление в первый из окружающих блоков, в котором встретится указанная метка. Управляющие структуры 3-15 Ограничения Некоторые возможные назначения предложения GOTO незаконны. В частности, предложение GOTO не может передавать управление в предложение IF, в предложение LOOP или в подблок. Например, следующее предложение GOTO незаконно: BEGIN ... GOTO update_row; -- незаконный переход в предложение IF ... IF valid THEN ... <> UPDATE emp SET ... END IF; END; Далее, предложение GOTO не может передавать управление из одной фразы предложения IF в другую, как показывает следующий пример: BEGIN ... IF valid THEN ... GOTO update_row; -- незаконный переход в фразу ELSE ELSE ... <> UPDATE emp SET ... END IF; END; Следующий пример показывает, что предложение IF не может передавать управление из окружающего блока в подблок: BEGIN ... IF status = 'OBSOLETE' THEN GOTO delete_part; -- незаконный переход в подблок END IF; ... BEGIN ... <> DELETE FROM parts WHERE ... END; END; 3-16 Руководство пользователя и справочник по PL/SQL Кроме того, предложение GOTO не может передавать управление наружу из подпрограммы, как показывает следующий пример: DECLARE ... PROCEDURE compute_bonus (emp_id NUMBER) IS BEGIN ... GOTO update_row; -- незаконный переход из подпрограммы END; BEGIN ... <> UPDATE emp SET ... END; Наконец, предложение GOTO не может передавать управление из обработчика исключений в текущий блок. Например, следующее предложение GOTO незаконно: DECLARE ... pe_ratio REAL; BEGIN ... SELECT price / NVL(earnings, 0) INTO pe_ratioo FROM ... <> INSERT INTO stats VALUES (pe_ratio, ...); EXCEPTION WHEN ZERO_DIVIDE THEN pe_ratio := 0; GOTO insert_row; -- незаконный переход в текущий блок ... END; Однако предложение GOTO может передавать управление из обработчика исключений в окружающий блок. Советы Чрезмерное применение предложений GOTO может привести к сложному, неструктурированному коду, который трудно понимать и сопровождать. Поэтому старайтесь использовать предложения GOTO пореже. Например, чтобы перейти из глубоко вложенной структуры на программу обработки ошибок, возбуждайте исключение, вместо того чтобы использовать предложение GOTO. Управляющие структуры 3-17 Предложение NULL ---------------- Предложение NULL явно специфицирует отсутствие действия; оно ничего не делает, и управление передается на следующее предложение. Оно, однако, может улучшить читабельность программы. В конструкте, допускающем альтернативные действия, предложение NULL используется для обозначения места действия. Оно напоминает читателю программы о том, что соответствующая альтернатива не была пропущена нечаянно, а просто не требует никакого действия. В следующем примере предложение NULL показывает, что для непоименованных исключений никакие действия не выполняются: ... EXCEPTION WHEN ZERO_DIVIDE THEN ROLLBACK; WHEN CALUE_ERROR THEN INSERT INTO errors VALUES ... COMMIT; WHEN OTHERS THEN NULL; END; Каждая фраза в предложении IF должны содержать хотя бы одно выполнимое предложение. Предложение NULL помогает удовлетворить этому требованию. Поэтому вы можете использовать предложение NULL в фразах, соответствующих тем обстоятельствам, в которых никакий действий не требуется. В следующем примере предложение NULL подчеркивает, что премии получат лишь сотрудники с высоким рейтингом: IF rating > 90 THEN compute_bonus(emp_id); ELSE NULL; END IF; Предложение NULL предоставляет также удобный способ создания "затычек" при разработке приложения сверху вниз. Затычки - это фиктивные подпрограммы, с помощью которых вы откладываете определения процедур и функций до тех пор, пока не проверите и не отладите главную программу. В следующем примере предложение NULL помогает удовлетворить тому требованию языка, что выполнимая часть подпрограммы должна содержать хотя бы одно предложение: PROCEDURE debit_account (acct_id INTEGER, amount REAL) IS BEGIN NULL; END debit_account;