РУКОВОДСТВО ПО РЕЛЯЦИОННОЙ СУБД DB2

         

ИСЧЕРПЫВАЮЩИЙ ПРИМЕР


Эту главу мы завершим надуманным, но исчерпывающим примером (рис. 10.3), позволяющим проиллюстрировать ряд дополнительных моментов. Рассматриваемая программа получает четыре значения входных данных: номер детали (ЗАДАННАЯ_ДЕТАЛЬ), название города (ЗАДАННЫЙ_ГОРОД), прирост состояния (ЗАДАННЫЙ_ПРИРОСТ) и уровень состояния (ЗАДАННЫЙ_УРОВЕНЬ). Программа просматривает всех поставщиков детали, идентифицируемой значением ЗАДАННАЯ_ДЕТАЛЬ. Для каждого такого поставщика состояние увеличивается на заданный прирост, если он находится в городе ЗАДАННЫЙ_ГОРОД. В противном случае, если состояние поставщика меньше значения ЗАДАННЫЙ_УРОВЕНЬ, поставщик удаляется вместе со всеми его поставками. Во всех случаях информация о поставщике выводится на печать с указанием, каким образом программа поступила с этим конкретным поставщиком.

SQLEX:         PROC     OPTIONS (MAIN);

DCL        ЗАДАННАЯ_ДЕТАЛЬ        CHAR 16);

DCL        ЗАДАННЫЙ_ГОРОД         CHAR (16);

DCL        ЗАДАННЫЙ_ПРИРОСТ    FIXED BINARY (15);

DCL        ЗАДАННЫЙ_УРОВЕНЬ    FIXED BINARY (16);

DCL        HOMEP_ ПОСТАВЩИКА                 CHAR (5);

DCL        ФАМИЛИЯ                           CHAR (20);

DCL        СОСТОЯНИЕ                       FIXED BINABY(15);

DCL        ГОРОД                                   CHAR 115);

DCL        ДИСПОЗИЦИЯ                    CHAR (7);

DCL        ДРУГИЕ_ПОСТАВЩИКИ ВIТ(1);

EXEC      SQL        INCLUDE              SQLCA;



EXEC      SQL        DECLARE              S TABLE

(НОМЕР_ПОСТАВЩИКА                 CHAR (5) NOT NULL,

ФАМИЛИЯ                           CHAR (20),

СОСТОЯНИЕ                       SMALLINT,

ГОРОД                                   CHAR (20));

EXEC      SQL        DECLARE              SP TABLE

(НОМЕР_ПОСТАВЩИКА                 CHAR (5) NOT NULL.

НОМЕР_ДЕТАЛИ                CHAR (6) NOT NULL,

КОЛИЧЕСТВО                     INTEGER);

EXEC      SQL        DECLARE              Z  CURSOR           FOR

SELECT                  HOMEP_ПОСТАВЩИКА, ФАМИЛИЯ, СОСТОЯНИЕ,


ГОРОД

FROM                    S

WHERE                  EXISTS

(SELECT

FROM                                    SP

WHERE                  SP. НОМЕР_ПОСТАВЩИКА=

S.HOMEP_ПОСТАВЩИКА

AND                       SP. НОМЕР_ДЕТАЛИ =

:ЗАДАННЫЙ_НОМЕР)

FOR                        update OF СОСТОЯНИЕ ;

EXEC      SQL        WHENEVER NOT FOUND CONTINUE;

EXEC      SQL        WHENEVER SQLERROR CONTINUE;

ЕХEС      SQL        whenever SQLWARNING CONTINUE;

ON          CONDITION         (DBEXCEPTION)

BEGIN:

PUT SKIP LIST (SQLCA);

EXEC SQL ROLLBACK;

GO TO QUIT;

END;

GET        LIST        (ЗАДАННАЯ_ДЕТАЛЬ, ЗАДАННЫЙ_ГОРОД, ЗАДАННЫЙ_ПРИРОСТ,

ЗАДАННЫЙ_УРОВЕНЬ);

EXEC      SQL        OPEN Z;    

IF SQLCODE Ø= 0

THEN SIGNAL CONDITION (DBEXCEPTION)

ДРУГИЕ_ПОСТАВЩИКИ =‘1’ B;

DO          WHILE (ДРУГИЕ_ПОСТАВЩИКИ);

EXEC      SQL        FETCH Z INTO

  

:НОМЕР_ПОСТАВЩИКА,

:ФАМИЛИЯ,

:СОСТОЯНИЕ,

:ГОРОД;

SELECT;                                                                                 /*SELECT языка ПЛ/1, а не SQL*/

WHEN                    (SQLCODE Ø= 100)

ДРУГИЕ_ПОСТАВЩИКИ = '0'B

WHEN                    (SQLCODE =100 & SOLCODE Ø= 0)

SIGNAL CONDITION (DBEXCEPTION);

WHEN-                   (SQLCODE =0)

DO;

ДИСПОЗИЦИЯ = ‘bbbbbbb ';

IF ГОРОД = ЗАДАННЫЙ_ ГОРОД

THEN

  DO;

EXEC SQL UPDATE            S

SET СОСТОЯНИЕ = СОСТОЯНИЕ

+ : ЗАДАННЫЙ_ПРИРОСТ

WHERE CURRENT OF Z ;

IF SQLCODE Ø=0

THEN SIGNAL CONDITION (DBEXCEPTION);

ДИСПОЗИЦИЯ = 'ОБНОВЛЕН ' ;

END;

         ELSE

IF СОСТОЯНИЕ < ЗАДАННЫЙ_УРОВЕНЬ

THEN

    DO; 

       EXEC SQL      DELETE

FROM    SP

WHERE НОМЕР_ПОСТАВЩИКА =

: НОМЕР_ПОСТАВЩИКА;

      IF SQLCODE Ø= 0 & SQLCODE Ø= 100

      THEN SIGNAL CONDITION (DBEXCEPTION);

     EXEC SQL        DELETE

FROM    S

WHERE  CURRENT OF Z;

     IF SQLCODE Ø= 0

     THEN SIGNAL CONDITION (DBEXCEPTION);

     ДИСПОЗИЦИЯ= ‘УДАЛЕН';

   END;

PUT SKIP LIST     (НОМЕР_ПОСТАВЩИКА, ФАМИЛИЯ, СОСТОЯНИЕ, ГОРОД,



ДИСПОЗИЦИЯ);

END; /*WHEN (SQLCODE=0) ...* /

END; /*SELECT языка ПЛ/1 */

END; /*DO  WHILE* /

EXEC SQL CLOSE Z;

EXEC SOL COMMIT;

QUIT: RETURN;

END; /*SQLEX */

Рис. 10.3. Исчерпывающий пример

Возникают следующие проблемы.

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

2. Далее обратите внимание на два предложения DECLARE для таблиц S и SP. Эти объявления, очевидно, представляют собой не что иное, как небольшие текстуальные вариации соответствующих предложений CREATE TABLE языка SQL. Для построения таких деклараций в интересах пользователей предусматривается специальная служебная программа — генератор деклараций (DCLGEN).

Примечание.

Название DCLGEN обычно произносится «деклеген» с мягким «г».

По существу, DCLGEN использует информацию из каталога системы DB2 для построения одной или обеих следующих синтаксических конструкций:

— предложения DECLARE для данной таблицы

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

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

ЕХЕС SQL INCLUDE раздел;

где «раздел» — это имя рассматриваемого раздела.

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

3. Как уже указывалось в разделе 10.2, каждое предложение языка SQL в принципе должно сопровождаться проверкой возвращаемого значения SQLCODE. Для того чтобы упростить этот процесс, предусмотрено предложение WHENEVER (всякий раз, когда). Это предложение имеет следующий синтаксис:



ЕХЕС SQL WHENEVER условие действие;

где «условие» может быть одним из следующих:

NOT FOUND (не найдено)

SQLWARNING (SQL-предупреждение)

SQLERROR (SQL-ошибка),

а «действие»—это либо предложение CONTINUE (продолжать), либо предложение GO TO (перейти к). Предложение WHENEVER не является исполняемым предложением. Оно представляет собой, скорее, директиву для прекомпилятора. Предложение «WHENEVER условие GO TO метка» заставляет прекомпилятор вставлять после каждого встречающегося ему исполняемого предложения языка SQL предложение вида «IF условие GO TO метка». В свою очередь предложение «WHENEVER условие CONTINUE» заставляет прекомпилятор не вставлять какие-либо такие предложения в связи с тем, что программист вставит их вручную. Указанные выше три «условия» определяются следующим образом:

NOT FOUND       означает, что SQLCODE = 100

SQLWARNING    означает, что SQLCODE > 0 и SQLCODE Ø= 100

SQLERROR          означает, что SQLCODE < О

Каждое предложение

WHENEVER, которое встречается прекомпилятору при последовательном просмотре им текста программы (для конкретного условия), отменяет действие предыдущего найденного им такого предложения (для этого условия). Предполагается, что в начале текста программы для каждого из трех возможных условий имеется неявное предложение WHENEVER, специфицирующее в каждом случае CONTINUE.

В приведенном примере программы в учебных целях явным образом делаются все проверки на исключительную ситуацию. Если какая-либо исключительная ситуация имеет место, управление передается процедуре, которая печатает диагностическую информацию (в данном примере, область связи SQL), издает команду ROLLBACK (см. п. 4 ниже), а затем передает управление заключительному RETURN.

4. Когда программа некоторым образом обновляет базу данных, такое обновление следует рассматривать первоначально лишь как предварительное — предварительное в том смысле, что если что-либо в дальнейшем выполнится с ошибкой, это обновление может быть аннулировано самой программой или системой. Если, например, в программе встречается неожиданная ошибка, скажем, переполнение, и она аварийно завершается, то система от имени программы автоматически аннулирует все такие предварительные обновления. Обновления остаются предварительными до тех пор, пока не произойдет одно из двух: а) исполнится предложение COMMIT (фиксировать), которое все предварительные обновления сделает окончательными («зафиксированными»), или б) исполнится предложение ROLLBACK (откат), которое аннулирует все предварительные обновления. После того как обновление зафиксировано, гарантируется, что оно никогда не будет аннулировано (это определение понятия «зафиксированное обновление»).



В рассматриваемом примере программа издает COMMIT, когда она приходит к своему нормальному завершению, и издает ROLLBACK, если встречается какая-либо исключительная ситуация при исполнении предложений SQL. На самом деле, такая явная спецификация предложения COMMIT не является необходимой. Система автоматически будет издавать COMMIT от имени программы для любой программы, которая достигает нормального завершения. Она также автоматически будет издавать ROLLBACK от имени программы для любой программы, которая не достигает нормального завершения. В данном примере, однако, обязательна явная спецификация предложения ROLLBACK, поскольку программа спроектирована таким образом, чтобы осуществлять нормальное ее завершение даже в случае, если имеют место исключительные ситуации SQL.

Примечание.

Вопросы обновления рассматривались выше в предположении обстановки TSO, поскольку предложения COMMIT и ROLLBACK допустимы только в этом случае. Эффект выполнения этих предложений в обстановке IMS и CICS достигается соответствующими обращениями (с помощью CALL) к IMS и CICS. Значительно более глубокое и полное обсуждение вопроса о «зафиксированных обновлениях» и связанного с этим понятия обработки транзакций приводится в следующей главе.


Содержание раздела