ヘッダーをスキップ
Pro*COBOLプログラマーズ・ガイド
11g リリース1(11.1)
E05690-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

7 ホスト表

この章では、ホスト表を使用してコーディングを単純化し、プログラムのパフォーマンスを向上させる方法を説明します。ここで紹介するのは、ホスト表を使用してOracleデータを操作する方法、単一のSQL文を使用してホスト表内のすべての要素を処理する方法、処理対象となる表の要素数を制限する方法、およびグループ項目の表の使用方法です。

この章の構成は、次のとおりです。

ホスト表

ホスト表(配列とも呼ぶ)とは、関連するデータ項目(要素と呼ぶ)のセットを1つの変数名に関連付けたものです。標識変数を表として定義した場合、その標識変数をインジケータ表と呼びます。インジケータ表は、NULLABLEである任意のホスト表に関連付けできます。

ホスト表の利点

ホスト表列を使用することにより、プログラミングが容易になり、パフォーマンスが大幅に向上します。通常、アプリケーションの作成には、大量のデータの格納と操作の問題が発生します。ホスト表を使用すれば、複数の戻り値へのアクセスが簡単になります。

ホスト表を使用すると、1つのSQL文で複数行を操作できます。このため、特にネットワーク化された環境では、通信オーバーヘッドが著しく低減されます。たとえば、およそ300人の従業員に関する情報をEMPという表に挿入する必要があるとします。ホスト表を使用しない場合は、プログラムでは従業員ごとに1回ずつ、合計300回INSERTを実行する必要があります。ホスト表を使用した場合は、INSERTを1回実行するだけです。

DML文の表

Pro*COBOLでは、DML文でホスト表を使用できます。ホスト表は、INSERT、UPDATEおよびDELETE文の入力変数として、また、SELECT文およびFETCH文のINTO句の出力変数として使用できます。

ホスト表に使用する構文は、通常のホスト変数に使用する構文とほとんど同じです。唯一の違いは、オプションのFOR句を使用して表の処理を制御できることです。また、ホスト表と通常のホスト変数を1つのSQL文で併用する場合にはいくつかの制限があります。

ホスト表の宣言

ホスト表の宣言およびディメンション指定はデータ部で行います。次の例では、3つのホスト表を宣言し、各表のディメンションは50要素に指定されます。

     ....
 01  EMP-TABLES.
     05  EMP-NUMBER  OCCURS 50 TIMES PIC S9(4) COMP.
     05  EMP-NAME    OCCURS 50 TIMES PIC X(10.
     05  SALARY      OCCURS 50 TIMES PIC S9(5)V99 COMP-3.
     ....

次の例に示すように、OCCURS句でINDEXED BY句を使用すると、索引を指定できます。

     ...
 01  EMP-TABLES.
     05  EMP-NUMBER  PIC X(10) OCCURS 50 TIMES
         INDEXED BY EMP-INDX.
             ...
     ...

このINDEXED BY句によって、索引項目EMP-INDXが暗黙的に宣言されます。

制限

多次元のホスト表は作成できません。したがって、次の例で宣言されている、2次元のホスト表は無効です。

     ...
 01  NATION.
     05  STATE                OCCURS 50 TIMES.
         10  STATE-NAME       PIC X(25).
         10  COUNTY           OCCURS 25 TIMES.
             15  COUNTY-NAME  PIX X(25).
     ...

また、可変長のホスト表も使用できません。たとえば、次に示すEMP-RECの宣言はホスト変数としては無効です。

     ...
 01  EMP-FILE.
     05  REC-COUNT  PIC S9(3) COMP.
     05  EMP-REC    OCCURS 0 TO 250 TIMES
         DEPENDING ON REC-COUNT.
     ...

1つのSQL文で、1回のフェッチ操作によってアクセスできるホスト表の要素の最大数は32000です(使用するプラットフォームと使用可能なメモリーによっては、32000以上になります)。最大数を超える数のホスト表要素にアクセスすると、パラメータが範囲外であることを示すランタイム・エラーが表示されます。文が無名PL/SQLブロックの場合、アクセス可能な要素の数は、32512をデータ型のサイズで割った数に制限されます。

ホスト表の参照

また、単一のSQL文で複数のホスト表を使用する場合は、各表のディメンションは同じであることが必要です。ただし、これはPro*COBOLでは常に最小のディメンションがSQL操作に使用されるためであり、必要条件ではありません。次の例では、挿入されるのは25行のみです。

 WORKING-STORAGE SECTION.
     EXEC SQL BEGIN DECLARE SECTION END-EXEC.
 01  EMP-TABLES.
     05  EMP-NUMBER   PIC S9(4) COMP OCCURS 50 TIMES.
     05  EMP-NAME     PIC X(10) OCCURS 50 TIMES.
     05  DEPT-NUMBER  PIC S9(4) COMP OCCURS 25 TIMES.
     EXEC SQL END DECLARE SECTION END-EXEC.
     ...
 PROCEDURE DIVISION.
     ...
*    Populate host tables here.
     ...
     EXEC SQL INSERT INTO EMP (EMPNO, ENAME, DEPTNO)
         VALUES (:EMP-NUMBER, :EMP-NAME, :DEPT-NUMBER)
     END-EXEC.

SQL文ではホスト表に添字を付けることはできません。たとえば、次のINSERT文は無効です。

 WORKING-STORAGE SECTION.
     EXEC SQL BEGIN DECLARE SECTION END-EXEC.
 01  EMP-TABLES.
     05  EMP-NUMBER   PIC S9(4) COMP OCCURS 50 TIMES.
     05  EMP-NAME     PIC X(10) OCCURS 50 TIMES.
     05  DEPT-NUMBER  PIC S9(4) COMP OCCURS 50 TIMES.
     EXEC SQL END DECLARE SECTION END-EXEC.
     ...
 PROCEDURE DIVISION.
     ...
     PERFORM LOAD-EMP VARYING J FROM 1 BY 1 UNTIL J > 50.
     ...
 LOAD-EMP.
     EXEC SQL INSERT INTO EMP (EMPNO, ENAME, DEPTNO)
         VALUES (:EMP-NUMBER(J), :EMP-NAME(J),
             :DEPT-NUMBER(J))
     END-EXEC.

PERFORM VARYING文でホスト表を処理する必要はありません。SQL文では添字なしの表名を使用してください。Pro*COBOLでは、ディメンションnのホスト表を使用したSQL文は、n個の異なるスカラー・ホスト変数を使用してそのSQL文をn回実行した場合と同じです。(ただし、ホスト表を使用する方が効率的です。)

インジケータ表の使用方法

インジケータ表を使用すると、入力ホスト表の要素にNULLを割り当てたり、出力ホスト表のNULLまたは切り捨てられた値(キャラクタ列のみ)を検出できます。次の例は、インジケータ表を使用してINSERTを実行する方法を示したものです。

 WORKING-STORAGE SECTION.
     EXEC SQL BEGIN DECLARE SECTION END-EXEC.
 01  EMP-TABLES.
     05  EMP-NUMBER   PIC S9(4) COMP OCCURS 50 TIMES.
     05  DEPT-NUMBER  PIC S9(4) COMP OCCURS 50 TIMES.
     05  COMMISSION   PIC S9(5)V99 COMP-3 OCCURS 50 TIMES.
     05  COMM-IND     PIC S9(4) COMP OCCURS 50 TIMES.
     EXEC SQL END DECLARE SECTION END-EXEC.
     ...
 PROCEDURE DIVISION.
     ...
*    Populate the host and indicator tables.
*    Set indicator table to all zeros.
     ...
     EXEC SQL INSERT INTO EMP (EMPNO, DEPTNO, COMM)
         VALUES (:EMP-NUMBER, :DEPT-NUMBER,
             :COMMISSION:COMM-IND)
     END-EXEC.

インジケータ表のディメンションは、ホスト表のディメンションと同じか、それよりも大きくする必要があります。

ホスト表SELECTおよびFETCHを使用する場合は、標識変数を使用することをお薦めします。インジケータ配列を使用すると、対応する出力ホスト表にNULLがあるかどうかをテストできます。

関連付けられた標識変数のないホスト変数にNULLが選択またはフェッチされると、プログラムは処理を停止し、sqlca.sqlerrd(3)を処理した行数に設定して、エラーを戻します。

デフォルトではNULLが選択されますが、UNSAFE_NULL = YESオプションを使用して、これをオフにすることもできます。

DBMS=V7またはV8のときは、切捨てはエラーとみなされません。

表を含んでいるホスト・グループ項目

注意: 表を含むホスト・グループ項目の場合は、インジケータに対応する表のグループ項目を使用する必要があります。たとえば、次のようなグループ項目を考えます。

 01  DEPARTURE.
     05 HOUR    PIC X(2) OCCURS 3 TIMES.
     05 MINUTE  PIC X(2) OCCURS 3 TIMES.

この場合、次の標識変数は使用できません。

 01  DEPARTURE-IND PIC S9(4) COMP OCCURS 6 TIMES.

表のグループ項目に使用する標識変数は、次のように、それ自体が表のグループ項目であることが必要です。

  01  DEPARTURE-IND.
      05 HOUR-IND   PIC S9(4) COMP OCCURS 3 TIMES.
      05 MINUTE-IND PIC S9(4) COMP OCCURS 3 TIMES.

Oracleでの制限

VALUES、SET、INTOまたはWHERE句では、スカラー・ホスト変数とホスト表を併用できません。ホスト変数のうちのどれか1つがホスト表の場合は、すべてのホスト変数がホスト表であることが必要です。

UPDATEまたはDELETE文のCURRENT OF句では、ホスト表は使用できません。

ANSIでの制限および要件

配列インタフェースは、ANSI/ISOの埋込みSQL規格に対するOracle拡張機能です。ただし、MODE=ANSIでプリコンパイルすると、配列のSELECTおよびFETCHは使用できます。必要であれば、FIPSフラガー・プリコンパイラ・オプションによって、配列を使用していることをフラグで示すことができます。

表に対する選択

ホスト表をSELECT文の出力変数として使用できます。選択で戻される行の最大数がわかっている場合は、それと同じ数の要素数でホスト表を定義してください。次の例では、選択結果を直接3つのホスト表に入れます。表は50行で定義されていますが、選択で戻されるのは50行以下です。

     01  EMP-REC-TABLES.
         05  EMP-NUMBER   OCCURS 50 TIMES PIC S9(4) COMP.
         05  EMP-NAME     OCCURS 50 TIMES PIC X(10) VARYING.
         05  SALARY       OCCURS 50 TIMES PIC S9(6)V99
                          DISPLAY SIGN LEADING SEPARATE.
     ...
     EXEC SQL SELECT ENAME, EMPNO, SAL
         INTO :EMP-NAME, :EMP-NUMBER, :SALARY
         FROM EMP
         WHERE SAL > 1000
     END-EXEC.

この例ではSELECT文は50行までの行を戻します。選択される行数が49行以下の場合、または50行のみを取り出す場合はこの方法を使用します。ただし、選択される行数が51行以上の場合は、この方法ではすべての行を取り出せません。このSELECT文を再実行しても、他に選択対象の行があるとしても、最初の50行のみがまた戻されます。このような場合は、大きな表を定義するか、FETCH文に使用するカーソルを宣言する必要があります。

SELECT_ERROR=NOと指定していない場合に、定義した表サイズよりもSELECT INTO文で戻される行数が多いと、Oracle9iはエラー・メッセージを出力します。詳細は、「SELECT_ERROR」を参照してください。

バッチ・フェッチ

処理するデータのサイズが大きい(100行以上)場合や戻される行数が不明な場合は、バッチ・フェッチを使用してください。

選択によって戻される行の最大数が不明な場合は、カーソルを宣言およびオープンして、そこからバッチでフェッチできます。ループ内でバッチ・フェッチを実行すると、多数の行を簡単に取り出せます。フェッチを行うたびに、現行のアクティブ・セットから次のバッチの行が戻されます。次の例では、20行ずつまとめて行をフェッチします。

 ...
 01  EMP-REC-TABLES.
     05  EMP-NUMBER    OCCURS 20 TIMES PIC S9(4) COMP.
     05  EMP-NAME      OCCURS 20 TIMES PIC X(10) VARYING.
     05  SALARY        OCCURS 20 TIMES PIC S9(6)V99
                       DISPLAY SIGN LEADING SEPARATE.
     ...
     EXEC SQL DECLARE EMPCURSOR CURSOR FOR
     SELECT EMPNO, SAL FROM EMP
     END-EXEC.
     ...
     EXEC SQL OPEN EMPCURSOR END-EXEC.
     ...
     EXEC SQL WHENEVER NOT FOUND DO PERFORM END-IT.
 LOOP.
     EXEC SQL FETCH EMPCURSOR INTO :EMP-NUMBER, :SALARY END-EXEC.
* --  process batch of rows
     ...
     GO TO LOOP.
 END-IT.
...

最後のフェッチで実際に戻された行数を必ずチェックし、その行を処理してください。詳細は、「サンプル・プログラム3: バッチでのフェッチ」を参照してください。

SQLERRD(3)の使用方法

INSERT、UPDATEおよびDELETE文では、処理された行数はSQLERRD(3)に格納されます。

SQLERRD(3)は、表の操作中にエラーが発生したときにも役に立ちます。処理はエラーを引き起こした行で停止するため、SQLERRD(3)を調べることによって正常に処理された行数がわかります。

フェッチされる行数

それぞれのフェッチで戻される行数は、最大でも表のエントリと同じ数です。次のような場合は最大行数より少ない行が戻ります。

  • アクティブ・セットの最後に達したとき:「データが見つかりません。」という警告コードがSQLCAのSQLCODEに戻されます。たとえば、エントリ数が100の表に対してフェッチを行い20行しか戻らなかった場合です。

  • フェッチ対象の行がバッチ行数よりも少ないとき:たとえば、エントリ数が20の表に対して70行をフェッチした場合、3回目のフェッチの後ではフェッチ対象の行が10行しか残っていないため、このような状況が発生します。

  • 行の処理中にエラーが検出されたとき:この場合、フェッチは失敗し、該当するエラー・コードがSQLCODEに戻されます。

戻された行の累計数は、SQLCA内のSQLERRDの3番目の要素(このマニュアルではSQLERRD(3)と呼びます)に格納されます。これはオープン状態のすべてのカーソルに適用されます。次の例では、カーソルの状態がそれぞれ更新されている様子がわかります。

     EXEC SQL OPEN CURSOR1 END-EXEC.
     EXEC SQL OPEN CURSOR2 END-EXEC.
     EXEC SQL FETCH CURSOR1 INTO :TABLE-OF-20 END-EXEC.
* --  now running total in SQLERRD(3) is 20
     EXEC SQL FETCH CURSOR2 INTO :TABLE-OF-30 END-EXEC.
* --  now running total in SQLERRD(3) is 30, not 50
     EXEC SQL FETCH CURSOR1 INTO :TABLE-OF-20 END-EXEC.
* --  now running total in SQLERRD(3) is 40 (20 + 20)
     EXEC SQL FETCH CURSOR2 INTO :TABLE-OF-30 END-EXEC.
* --  now running total in SQLERRD(3) is 60 (30 + 30)

ホスト表の使用制限

SELECT文のWHERE句でホスト表を使用できるのは、副問合せの場合のみです。「WHERE句」の例を参照してください。また、Pro*COBOLでは常に表の最小ディメンションが使用されるため、SELECT文またはFETCH文のINTO句では通常のホスト変数とホスト表を併用しないでください。そのようにすると、1行しか取り出されません。ホスト変数のうちのどれか1つが表の場合は、すべてのホスト変数が表であることが必要です。

表7-1に、SELECT INTO文に有効なホスト表の使用方法を示します。

表7-1 SELECT INTO文に有効なホスト表

INTO句 WHERE句 有効/無効

無効

スカラー

スカラー

有効

スカラー

有効

スカラー

無効


NULLのフェッチ

UNSAFE_NULL=YESの場合は、インジケータ表のないホスト表に対してNULLを選択またはフェッチしてもエラーが生成されません。したがって、表の選択およびフェッチを行うときには、インジケータ表の使用をお薦めします。これにより、対応付けられた出力ホスト表のNULLが容易に検索できるようになります。(NULLおよび切り捨てられた値の検出方法は、「標識変数の使用方法」を参照してください。)

UNSAFE_NULL=NOの場合は、インジケータ表のないホスト表に対してNULLを選択またはフェッチすると、Oracle9iは処理を停止し、処理した行数をSQLERRD(3)に設定して、エラー・メッセージを出力します。

切り捨てられた値のフェッチ

インジケータ表のないホスト表に対して切り捨てられた列値を選択またはフェッチすると、Oracle9iはSQLWARN(2)を設定します。

SQLERRD(3)を調べると、切捨てが行われるまでに処理された行数がわかります。処理済行数には切捨てエラーが発生した行も含まれます。

表の選択およびフェッチを行うときには、インジケータ表を使用できます。インジケータ表を使用すると、Oracle9iが1つ以上の切り捨てられた列値を出力ホスト表に割り当てた場合に、対応するインジケータ表を調べることによってその列値の元の長さを確認できます。

サンプル・プログラム3: バッチでのフェッチ

次のホスト表のサンプル・プログラムは、デモ・ディレクトリに入っています。

      *****************************************************************
      * Sample Program 3:  Host Tables                                *
      *                                                               *
      * This program logs on to ORACLE, declares and opens a cursor,  *
      * fetches in batches using host tables, and prints the results. *
      *****************************************************************

       IDENTIFICATION DIVISION.
       PROGRAM-ID. HOST-TABLES.
       ENVIRONMENT DIVISION.
       DATA DIVISION.
       WORKING-STORAGE SECTION.

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.
       01  USERNAME          PIC X(15) VARYING.
       01  PASSWD            PIC X(15) VARYING.
       01  EMP-REC-TABLES.
           05  EMP-NUMBER    OCCURS 5 TIMES PIC S9(4) COMP.
           05  EMP-NAME      OCCURS 5 TIMES PIC X(10) VARYING.
           05  SALARY        OCCURS 5 TIMES PIC S9(6)V99
                             DISPLAY SIGN LEADING SEPARATE.
           EXEC SQL VAR SALARY IS DISPLAY(8,2) END-EXEC.
           EXEC SQL END DECLARE SECTION END-EXEC.
           EXEC SQL INCLUDE SQLCA END-EXEC.
       01  NUM-RET           PIC S9(9) COMP VALUE ZERO.
       01  PRINT-NUM         PIC S9(9) COMP VALUE ZERO.
       01  COUNTER           PIC S9(9) COMP.
       01  DISPLAY-VARIABLES.
           05  D-EMP-NAME    PIC X(10).
           05  D-EMP-NUMBER  PIC 9(4).
           05  D-SALARY      PIC Z(4)9.99.

       PROCEDURE DIVISION.

       BEGIN-PGM.
           EXEC SQL
               WHENEVER SQLERROR DO PERFORM SQL-ERROR
           END-EXEC.
           PERFORM LOGON.
           EXEC SQL
               DECLARE C1 CURSOR FOR
               SELECT EMPNO, SAL, ENAME
               FROM EMP
           END-EXEC.
           EXEC SQL
               OPEN C1
           END-EXEC.

       FETCH-LOOP.
           EXEC SQL
               WHENEVER NOT FOUND DO PERFORM SIGN-OFF
           END-EXEC.
           EXEC SQL
               FETCH C1
               INTO :EMP-NUMBER, :SALARY, :EMP-NAME
           END-EXEC.
           SUBTRACT NUM-RET FROM SQLERRD(3) GIVING PRINT-NUM.
           PERFORM PRINT-IT.
           MOVE SQLERRD(3) TO NUM-RET.
           GO TO FETCH-LOOP.

       LOGON.
           MOVE "SCOTT" TO USERNAME-ARR.
           MOVE 5 TO USERNAME-LEN.
           MOVE "TIGER" TO PASSWD-ARR.
           MOVE 5 TO PASSWD-LEN.
           EXEC SQL
              CONNECT :USERNAME IDENTIFIED BY :PASSWD
           END-EXEC.
           DISPLAY " ".
           DISPLAY "CONNECTED TO ORACLE AS USER:  ", USERNAME-ARR.

       PRINT-IT.
           DISPLAY " ".
           DISPLAY "EMPLOYEE NUMBER  SALARY   EMPLOYEE NAME".
           DISPLAY "---------------  -------  -------------".
           PERFORM PRINT-ROWS
               VARYING COUNTER FROM 1 BY 1
               UNTIL COUNTER > PRINT-NUM.

       PRINT-ROWS.
           MOVE EMP-NUMBER(COUNTER) TO D-EMP-NUMBER.
           MOVE SALARY(COUNTER) TO D-SALARY.
           DISPLAY "           ", D-EMP-NUMBER, " ", D-SALARY, "  ",
               EMP-NAME-ARR IN EMP-NAME(COUNTER).
           MOVE SPACES TO EMP-NAME-ARR IN EMP-NAME(COUNTER).

       SIGN-OFF.
           SUBTRACT NUM-RET FROM SQLERRD(3) GIVING PRINT-NUM.
           IF (PRINT-NUM > 0) PERFORM PRINT-IT.
           EXEC SQL
               CLOSE C1
           END-EXEC.
           EXEC SQL
               COMMIT WORK RELEASE
           END-EXEC.
           DISPLAY " ".
           DISPLAY "HAVE A GOOD DAY.".
           DISPLAY " ".
           STOP RUN.

       SQL-ERROR.
           EXEC SQL
               WHENEVER SQLERROR CONTINUE
           END-EXEC.
           DISPLAY " ".
           DISPLAY "ORACLE ERROR DETECTED:".
           DISPLAY " ".
           DISPLAY SQLERRMC.
           EXEC SQL
               ROLLBACK WORK RELEASE
           END-EXEC.
           STOP RUN.

表に対する挿入

ホスト表は、INSERT文の入力変数として使用できます。その場合、INSERT文の実行前に表にデータが挿入されるようにプログラムを作成してください。表の中に不適切な要素がある場合は、FOR句を使用して挿入する行数を制御できます。「FOR句」を参照してください。

次に、ホスト表に対する挿入の例を示します。

 01  EMP-REC-TABLES.
     05  EMP-NUMBER    OCCURS 50 TIMES PIC S9(4) COMP.
     05  EMP-NAME      OCCURS 50 TIMES PIC X(10) VARYING.
     05  SALARY        OCCURS 50 TIMES PIC S9(6)V99
                       DISPLAY SIGN LEADING SEPARATE.
* -- populate the host tables
     EXEC SQL INSERT INTO EMP (ENAME, EMPNO, SAL)
         VALUES (:EMP-NAME, :EMP-NUMBER, :SALARY)
     END-EXEC.

挿入された行数は、SQLERRD(3)を調べます。

SQL文ではホスト表に添字を付けることはできません。たとえば、次のINSERT文は無効です。

     PERFORM VARYING I FROM 1 BY 1 UNTIL I = TABLE-DIMENSION.
        EXEC SQL INSERT INTO EMP (ENAME, EMPNO, SAL)
            VALUES (:EMP-NAME(I), :EMP-NUMBER(I), :SALARY(I))
        END_EXEC
     END-PERFORM.

ホスト表の使用制限

INSERT、UPDATEまたはDELETE文のVALUES句に単純ホスト変数とホスト表を併用すると、ホスト表の最初の要素のみ処理されます。Pro*COBOLは常に宣言された最小のディメンションを使用し、単純ホスト変数はディメンション1のホスト表として扱われるためです。この場合には警告が表示されます。

表に対する更新

次の例に示すように、UPDATE文の入力変数としてもホスト表を使用できます。

 01  EMP-REC-TABLES.
     05  EMP-NUMBER    OCCURS 50 TIMES PIC S9(4) COMP.
     05  SALARY        OCCURS 50 TIMES PIC S9(6)V99
                           DISPLAY SIGN LEADING SEPARATE.
 ...
* --  populate the host tables
     EXEC SQL
         UPDATE EMP SET SAL = :SALARY WHERE EMPNO = :EMP-NUMBER
     END-EXEC.

この文を発行して更新された行数は、SQLERRD(3)を調べます。更新された行数がホスト表の行数と同じとはかぎりません。その行数には、更新カスケード(後続の更新を実行させる)によって処理された行は含まれていません。

表の中に不適切な要素がある場合は、FOR句を使用することによって更新する行数を制限できます。

前述の例は、一意キー(EMP-NUMBER)を使用した典型的な更新を示しています。この例では、表の各要素によって更新されるのは1行のみです。次の例では、表の1つの要素によって複数行が削除されます。

     EXEC SQL BEGIN DECLARE SECTION END-EXEC.
     ...
            05  JOB-TITLE      OCCURS 10 TIMES PIC X(10) VARYING.
            05  COMMISSION     OCCURS 50 TIMES PIC S9(6)V99
                              DISPLAY SIGN LEADING SEPARATE.
     EXEC SQL END DECLARE SECTION END-EXEC.
* --  populate the host tables
     EXEC SQL
         UPDATE EMP SET COMM = :COMMISSION WHERE JOB = :JOB-TITLE
     END-EXEC.

UPDATEでの制限

UPDATE文のCURRENT OF句ではホスト表は使用できません。 他の方法は、「CURRENT OF句の擬似実行」を参照してください。

表7-2に、UPDATE文に有効なホスト表の使用方法を示します。

表7-2 UPDATE文に有効なホスト表

SET句 WHERE句 有効/無効

有効

スカラー

スカラー

有効

スカラー

無効

スカラー

無効


表に対する削除

ホスト表をDELETE文の入力変数としても使用できます。これは、WHERE句でそのホスト表の連続した要素を使用してDELETE文を繰返し実行するのと同様です。つまり1回の実行で表から0行、1行または複数行が削除されます。次に示すのは、ホスト表を使用した削除の例です。

     EXEC SQL BEGIN DECLARE SECTION END-EXEC.
     ...
         05  EMP-NUMBER    OCCURS 50 TIMES PIC S9(4) COMP.
     EXEC SQL END DECLARE SECTION END-EXEC.
* --  populate the host table
     EXEC SQL
         DELETE FROM EMP WHERE EMPNO = :EMP-NUMBER
     END-EXEC.

削除された行の累計数は、SQLERRD(3)を調べます。この累計数には、削除カスケードで処理された行は含まれません。

次に、一意キー(EMP-NUMBER)を使用した典型的な削除の例を示します。この例では、表の各要素によって削除されるのは1行のみです。次の例では、表の1つの要素によって複数行が削除されます。

     EXEC SQL BEGIN DECLARE SECTION END-EXEC.
     ...
            05  JOB-TITLE    OCCURS 10 TIMES PIC X(10) VARYING.
     EXEC SQL END DECLARE SECTION END-EXEC.
* --  populate the host table
     EXEC SQL
        DELETE FROM EMP WHERE JOB = :JOB-TITLE
     END-EXEC.

DELETEでの制限

DELETE文のCURRENT OF句ではホスト表は使用できません。他の方法は、「CURRENT OF句の擬似実行」を参照してください。

インジケータ表の使用方法

インジケータ表は、ホスト表にNULLを割り当てる場合や、出力ホスト表の中のNULLまたは切り捨てられた値の検出に使用します。次に、インジケータ表を使用した挿入方法の例を示します。

 01  EMP-REC-VARS.
     05  EMP-NUMBER  OCCURS 50 TIMES PIC S9(4) COMP.
     05  DEPT-NUMBER OCCURS 50 TIMES PIC S9(4) COMP.
     05  COMMISSION  OCCURS 50 TIMES  PIC S9(6)V99
                            DISPLAY SIGN LEADING SEPARATE.
* -- indicator table:
     05  COMM-IND    OCCURS 50 TIMES  PIC S9(4) COMP.
* --  populate the host tables
* --  populate the indicator table; to insert a NULL into
* --  the COMM column, assign -1 to the appropriate element in
* --  the indicator table
     EXEC SQL
         INSERT INTO EMP (EMPNO, DEPTNO, COMM)
         VALUES (:EMP_NUMBER, :DEPT-NUMBER, :COMMISSION:COMM-IND)
     END-EXEC.

インジケータ表のエントリ数をホスト表のエントリ数より少なくすることはできません。

FOR句

オプションのFOR句を使用すると、次のSQL文で処理する表要素の数を設定できます。

特にUPDATE、INSERTおよびDELETE文内でFOR句を使用すると便利です。これらの文では、表全体を使用する必要がない場合があります。次の例に示すように、FOR句を使用すると、使用する要素数を任意の数に制限できます。

     EXEC SQL BEGIN DECLARE SECTION END-EXEC.
 01  EMP-REC-VARS.
     05  EMP-NAME  OCCURS 1000 TIMES  PIC X(20) VARYING.
     05  SALARY    OCCURS 100  TIMES  PIC S9(6)V99
                   DISPLAY SIGN LEADING SEPARATE.
 01 ROWS-TO-INSERT PIC S9(4) COMP.
     EXEC SQL END DECLARE SECTION END-EXEC.
* --  populate the host tables
     MOVE 25 TO ROWS-TO-INSERT.
* -- set FOR-clause variable
* -- will process only 25 rows
     EXEC SQL FOR :ROWS-TO-INSERT
         INSERT INTO EMP (ENAME, SAL)
         VALUES (:EMP-NAME, :SALARY)
     END-EXEC.

FOR句では、表の要素数の指定には整数のホスト変数を使用する必要があります。たとえば、次の文は無効です。

* -- illegal
     EXEC SQL FOR 25
     INSERT INTO EMP (ENAME, EMPNO, SAL)
         VALUES (:EMP-NAME, :EMP-NUMBER, :SALARY)
     END-EXEC.

FOR句の変数によって、処理する表要素の数を指定します。その数は、表の最小ディメンションより大きくしないでください。内部では、値は符号なしの数量として扱われます。符号付きのホスト変数を使用して負の値を渡すと、予測不能な動作が発生する原因となります。

制限

FOR句のセマンティクスを明確にするための制限が2つあります。FOR句は、SELECT文での使用またはCURRENT OF句との併用はできません。

SELECT文での使用

SELECT文でFOR句を使用すると、エラー・メッセージが戻されます。

FOR句は意味が不明確なため、SELECT文では使用できません。このSELECT文をn回実行するのか、このSELECT文を1回実行してn行戻すのかが不明確です。前者の場合は、SELECT文を実行するたびに複数行が戻される可能性があります。後者の解釈では、次に示すように、カーソルを宣言してからFETCH文中でFOR句を使用することをお薦めします。

     EXEC SQL FOR :LIMIT FETCH EMPCURSOR INTO ...

CURRENT OF句との併用

次の例に示すように、UPDATEまたはDELETE文でCURRENT OF句を使用すると、FETCH文によって戻される最後の行を参照できます。

     EXEC SQL DECLARE EMPCURSOR CURSOR FOR
         SELECT ENAME, SAL FROM EMP WHERE EMPNO = :EMP-NUMBER
     END-EXEC.
     ...
     EXEC SQL OPEN EMPCURSOR END-EXEC.
     ...
     EXEC SQL FETCH emp_cursor INTO :EM-NAME, :SALARY END-EXEC.
     ...
     EXEC SQL UPDATE EMP SET SAL = :NEW-SALARY
         WHERE CURRENT OF EMPCURSOR
     END-EXEC.

ただし、CURRENT OF句とFOR句は併用できません。次の文はLIMITの論理値が1に限定されている(カレント行の更新または削除は1回しかできない)ため無効です。

     EXEC SQL FOR :LIMIT UPDA-CURSOR END-EXEC.
     ...
     EXEC SQL FOR :LIMIT DELETE FROM EMP
         WHERE CURRENT OF emp_cursor
     END-EXEC.

WHERE句

Pro*COBOLでは、エントリ数nのホスト表を使用したSQL文は、n個の異なるスカラー変数(表の個々の要素)を使用してそのSQL文をn回実行した場合と同じように扱われます。文意が不明瞭なためにこのような処理ができない場合にのみ、プリコンパイラはエラー・メッセージを出力します。

たとえば、次の宣言を指定するとします。

     EXEC SQL BEGIN DECLARE SECTION END-EXEC.
     ...
     05  MGRP-NUMBER  OCCURS 50 TIMES  PIC S9(4) COMP.
     05  JOB-TITLE    OCCURS 50 TIMES  PIC X(20) VARYING.
 01  I PIC S9(4) COMP.
     EXEC SQL END DECLARE SECTION END-EXEC.

この宣言部は、次の文を使用すると、不明瞭になる場合があります。

     EXEC SQL SELECT MGR INTO :MGR-NUMBER FROM EMP
         WHERE JOB = :JOB-TITLE
     END-EXEC.

この文が、次の文と同様に処理された場合です。

     PERFORM VARYING I FROM 1 BY 1 UNTIL I = 50
     SELECT MGR INTO :MGR-NUMBER(I) FROM EMP
         WHERE JOB = :JOB_TITLE(I)
     END-EXEC
     END-PERFORM.

WHERE句の検索条件を満たす複数行があっても、データの受取りに使用できる出力変数が1つしかないためです。したがって、エラー・メッセージが出力されます。

一方、次の文を使用すると、不明瞭にならない場合があります。

      EXEC SQL
         UPDATE EMP SET MGR = :MGR_NUMBER
         WHERE EMPNO IN (SELECT EMPNO FROM EMP WHERE
         JOB = :JOB-TITLE)
     END-EXEC.

この文が、次の文と同様に処理された場合です。

     PERFORM VARYING I FROM 1 BY 1 UNTIL I = 50
         UPDATE EMP SET MGR = :MGR_NUMBER(I)
             WHERE EMPNO IN
             (SELECT EMPNO FROM EMP WHERE JOB = :JOB-TITLE(I))
         END-EXEC
     END-PERFORM.

これは、WHERE句の各JOB-TITLEに該当する複数行がある場合でも、JOB-TITLEに該当する行のそれぞれに対してSET句内にMGR-NUMBERが存在するためです。それぞれのJOB-TITLEに該当するすべての行を、同じMGR-NUMBERにSETできます。このため、エラー・メッセージは出力されません。

CURRENT OF句の疑似実行

CURRENT OF句を使用すると、カーソルの最新の行に対してUPDATEまたはDELETEを実行できます。CURRENT OF句を使用すると、FOR UPDATE句がカーソルに追加されます。この句を追加することは、カーソルで識別されたすべての行を排他モードでロックする効果があります。CURRENT OF句はホスト表には使用できません。かわりに、カーソルの定義にFOR UPDATEを追加し、ROWID列を明示的に選択して、その値を使用して更新または削除の実行時にカレント行を識別できます。 次に、例を示します。

         05  EMP-NAME    OCCURS 25 TIMES PIC X(20) VARYING.
         05  JOB-TITLE   OCCURS 25 TIMES PIC X(15) VARYING.
         05  OLD-TITLE   OCCURS 25 TIMES PIC X(15) VARYING.
         05  ROW-ID      OCCURS 25 TIMES PIC X(18) VARYING.
     ...
     EXEC SQL DECLARE EMPCURSOR CURSOR FOR
         SELECT ENAME, JOB, ROWID FROM EMP
         FOR UPDATE
     END-EXEC.
     ...
     EXEC SQL OPEN EMPCURSOR END-EXEC.
     ...
     EXEC SQL WHENEVER NOT FOUND GOTO ...
     ...
     PERFORM
         EXEC SQL
             FETCH EMPCURSOR
             INTO :EMP-NAME, :JOB-TITLE, :ROW-ID
         END-EXEC
         ...
         EXEC SQL
             DELETE FROM EMP
             WHERE JOB = :OLD-TITLE AND ROWID = :ROW-ID
         END-EXEC
         EXEC SQL COMMIT WORK END-EXEC
      END-PERFORM.

ホスト変数としてのグループ項目の表

Pro*COBOLでは、埋込みSQL文でグループ項目(レコードとも呼ばれます)の表を使用できます。グループ項目の表は、SELECTまたはFETCH文のINTO句、およびINSERT文のVALUESリストで参照できます。

次に例を示します。

 01    TABLES.
       05   EMP-TABLE           OCCURS 20 TIMES.
            10    EMP-NUMBER    PIC S9(4) COMP.
            10    EMP-NAME      PIC X(10).
            10    DEPT-NUMBER   PIC S9(4) COMP.

このように宣言した場合、次の文は有効です。

       EXEC SQL INSERT INTO EMP(EMPNO, ENAME, DEPTNO)
            VALUES(:EMP-TABLE)
       END-EXEC.

グループ項目にデータがすでに入っているとみなして、この文によって、従業員番号、従業員名および部門番号で構成された行が20行一括してEMP表に挿入されます。

グループ項目の順序がSQL文内での順序と同じであることを確認してください。

標識変数を使用するには、グループ項目の各変数の標識変数を含むグループ項目表を別に設定します。

 01     TABLES-IND.
        05   EMP-TABLE-IND  OCCURS 20 TIMES.
             10   EMP-NUMBER-IND       PIC S9(4) COMP.
             10   EMP-NAME-IND         PIC S9(4) COMP.
             10   DEPT-NUMBER_IND      PIC S9(4) COMP.

グループ項目のホスト・インジケータ表は、次のように使用できます。

        EXEC SQL INSERT INTO EMP (EMPNO, ENAME, DEPTNO)
            VALUES (:EMP-TABLE:EMP-TABLE-IND)
        END-EXEC.

データの特徴が正確にわかっている場合は、基本項目のインジケータをグループ項目に指定すると便利です。

        05    EMP-TABLE-IND     PIC S9(4) COMP
                                OCCURS 20 TIMES.

グループ項目のホスト表には、表であるグループ項目を入れることはできません。 たとえば、次のようにはできません。

 01   TABLES.
      05   EMP-TABLE               OCCURS 20 TIMES.
           10  EMP-NUMBER          PIC S9(4) COMP OCCURS 10 TIMES.
           10  EMP-NAME            PIC X(10).
           10  DEPT-NUMBER         PIC S9(4) COMP.

EMP-NUMBERは表であるため、EMP-TABLEはホスト変数として使用できません。

ネストされたグループ項目のホスト表は使用できません。 たとえば、次のようにはできません。

 01   TABLES.
      05   TEAM-TABLE                   OCCURS 20 TIMES
           10   EMP-TABLE
                15   EMP-NUMBER         PIC S9(4) COMP.
                15   EMP-NAME           PIC X(10).
           10   DEPT-TABLE.
                15   DEPT-NUMBER        PIC S9(4) COMP.
                15   DEPT-NAME          PIC X(10).

メンバー(EMP-TABLEおよびDEPT-TABLE)自体がグループ項目であるため、TEAM-TABLEはホスト変数として使用できません。

また、Pro*COBOLでホスト表に適用される制限は、グループ項目の表にも適用されます。

サンプル・プログラム14: グループ項目の表

このプログラムで、ログインし、カーソルを宣言およびオープンし、グループ項目の表を使用してバッチでフェッチを行います。詳細は、最初のコメントを参照してください。

      *****************************************************************
      * Sample Program 14:  Tables of group items                     *
      *                                                               *
      * This program logs on to ORACLE, declares and opens a cursor,  *
      * fetches in batches using a table of group items , and prints  *
      * the results.  This sample is identical to sample3 except that *
      * instead of using three separate host tables of five elements  *
      * each, it uses a five-element table of three group items.      *
      * The output should be identical.                               *
      *****************************************************************

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TABLE-OF-GROUP-ITEMS.
       ENVIRONMENT DIVISION.
       DATA DIVISION.
       WORKING-STORAGE SECTION.

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.
       01  USERNAME          PIC X(15) VARYING.
       01  PASSWD            PIC X(15) VARYING.
       01  EMP-REC-TABLE OCCURS 5 TIMES.
           05  EMP-NUMBER    PIC S9(4) COMP.
           05  SALARY        PIC S9(6)V99
                             DISPLAY SIGN LEADING SEPARATE.
           05  EMP-NAME      PIC X(10) VARYING.
           EXEC SQL VAR SALARY IS DISPLAY(8,2) END-EXEC.
           EXEC SQL END DECLARE SECTION END-EXEC.
           EXEC SQL INCLUDE SQLCA END-EXEC.
       01  NUM-RET           PIC S9(9) COMP VALUE ZERO.
       01  PRINT-NUM         PIC S9(9) COMP VALUE ZERO.
       01  COUNTER           PIC S9(9) COMP.
       01  DISPLAY-VARIABLES.
           05  D-EMP-NAME    PIC X(10).
           05  D-EMP-NUMBER  PIC 9(4).
           05  D-SALARY      PIC Z(4)9.99.

       PROCEDURE DIVISION.

       BEGIN-PGM.
           EXEC SQL
               WHENEVER SQLERROR DO PERFORM SQL-ERROR
           END-EXEC.
           PERFORM LOGON.
           EXEC SQL
               DECLARE C1 CURSOR FOR
               SELECT EMPNO, SAL, ENAME
               FROM EMP
           END-EXEC.
           EXEC SQL
               OPEN C1
           END-EXEC.

       FETCH-LOOP.
           EXEC SQL
               WHENEVER NOT FOUND DO PERFORM SIGN-OFF
           END-EXEC.
           EXEC SQL
               FETCH C1
               INTO :EMP-REC-TABLE
           END-EXEC.
           SUBTRACT NUM-RET FROM SQLERRD(3) GIVING PRINT-NUM.
           PERFORM PRINT-IT.
           MOVE SQLERRD(3) TO NUM-RET.
           GO TO FETCH-LOOP.

       LOGON.
           MOVE "SCOTT" TO USERNAME-ARR.
           MOVE 5 TO USERNAME-LEN.
           MOVE "TIGER" TO PASSWD-ARR.
           MOVE 5 TO PASSWD-LEN.
           EXEC SQL
              CONNECT :USERNAME IDENTIFIED BY :PASSWD
           END-EXEC.
           DISPLAY " ".
           DISPLAY "CONNECTED TO ORACLE AS USER:  ", USERNAME-ARR.

       PRINT-IT.
           DISPLAY " ".
           DISPLAY "EMPLOYEE NUMBER  SALARY   EMPLOYEE NAME".
           DISPLAY "---------------  -------  -------------".
           PERFORM PRINT-ROWS
               VARYING COUNTER FROM 1 BY 1
               UNTIL COUNTER > PRINT-NUM.

       PRINT-ROWS.
           MOVE EMP-NUMBER(COUNTER) TO D-EMP-NUMBER.
           MOVE SALARY(COUNTER) TO D-SALARY.
           DISPLAY "           ", D-EMP-NUMBER, " ", D-SALARY, "  ",
               EMP-NAME-ARR IN EMP-NAME(COUNTER).
           MOVE SPACES TO EMP-NAME-ARR IN EMP-NAME(COUNTER).

       SIGN-OFF.
           SUBTRACT NUM-RET FROM SQLERRD(3) GIVING PRINT-NUM.
           IF (PRINT-NUM > 0) PERFORM PRINT-IT.
           EXEC SQL
               CLOSE C1
           END-EXEC.
           EXEC SQL
               COMMIT WORK RELEASE
           END-EXEC.
           DISPLAY " ".
           DISPLAY "HAVE A GOOD DAY.".
           DISPLAY " ".
           STOP RUN.

       SQL-ERROR.
           EXEC SQL
               WHENEVER SQLERROR CONTINUE
           END-EXEC.
           DISPLAY " ".
           DISPLAY "ORACLE ERROR DETECTED:".
           DISPLAY " ".
           DISPLAY SQLERRMC.
           EXEC SQL
               ROLLBACK WORK RELEASE
           END-EXEC.
           STOP RUN.

配列INSERTおよび配列SELECT構文の追加

Oracleプリコンパイラでは、ホスト表に対するDB2のINSERTおよびFETCH構文もサポートされています。次に、サポートされている追加の配列INSERTおよび配列FETCH構文をそれぞれ次の図で示します。

図7-1 追加のINSERT構文

追加のINSERT構文
図7-1「追加のINSERT構文」の説明

図7-2 追加のFETCH構文

追加のFETCH構文
図7-2「追加のFETCH構文」の説明

オプションでROWSET句とROWSET STARTING AT句がフェッチの方向(FIRST, PRIOR, NEXT, LAST, CURRENT, RELATIVEおよびABSOLUTE)で使用されます。次の例を考えてみます。

表7-3に、DB2の配列INSERT構文、配列FETCH構文の例と、対応するOracleプリコンパイラの構文との比較を示します。

表7-3 DB2の配列構文とOracleプリコンパイラの構文

DB2の配列構文 Oracleプリコンパイラの構文
EXEC SQL
  INSERT INTO DSN8810.ACT
  (ACTNO, ACTKWD, ACTDESC)
  VALUES (:HVA1, :HVA2, :HVA3)
  FOR :NUM_ROWS ROWS
END-EXEC.
EXEC SQL FOR :NUM_ROWS
  INSERT INTO DSN8810.ACT
  (ACTNO, ACTKWD, ACTDESC)
  VALUES (:HVA1, :HVA2, :HVA3)
END-EXEC.
EXEC SQL
  FETCH NEXT ROWSET FROM C1
  FOR 20 ROWS
  INTO :HVA_EMPNO, :HVA_LASTNAME,
       :HVA_SALARY
END-EXEC.
EXEC SQL
   FOR :TWENTY
   FETCH c1
   INTO :HVA_EMPNO, :HVA_LASTNAME,
        :HVA_SALARY
END-EXEC.

DB2の構文では、データの行セットを取得する前に、行セットに位置付けられたカーソルを最初に宣言する必要があります。カーソルが行セットをフェッチできるようにするには、DECLARE CURSOR文で「WITH ROWSET POSITIONING」句を使用する必要があります。これは、次の表に示すように、Oracleプリコンパイラの構文では必要なく、関連もありません。

DB2の配列構文 Oracleプリコンパイラの構文
EXEC SQL
 DECLARE C1 CURSOR
  WITH ROWSET POSITIONING FOR
  SELECT EMPNO, LASTNAME, SALARY
      FROM DSN8810.EMP
END-EXEC.
EXEC SQL
   DECLARE C1 CURSOR FOR
   SELECT EMPNO, LASTNAME, SALARY
        FROM DSN8810.EMP
END-EXEC.

この追加の配列構文は、プリコンパイラのオプション「db2_array」を使用して有効化できます。このオプションのデフォルトは「no」です。サポートされるDB2の配列構文は、Oracleプリコンパイラの構文と一緒には使用できません。Oracleプリコンパイラの構文またはDB2の構文のいずれか1つの構文のみが一度にサポートされます。

例7-1 DB2の配列構文を使用した行の挿入およびフェッチ

このプログラムでは、DB2の配列INSERT構文を使用して、EMP表にINSCNT行を挿入します。その後、DB2の配列FETCH構文を使用して、挿入された行をフェッチします。

      *****************************************************************
      * db2arrdemo:                                                   *
      *****************************************************************

       IDENTIFICATION DIVISION.
       PROGRAM-ID.  db2arrdemo.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       DATA DIVISION.
       WORKING-STORAGE SECTION.

      * EMBEDDED COBOL (file "DB2ARRDEMO.PCO")

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.
       01  USERNAME          PIC X(10) VARYING.
       01  PASSWD            PIC X(10) VARYING.
       01  EMPINDATA.
           02  EMPIN OCCURS 25 TIMES.
              03  EMPNO PIC  9(4) COMP.
              03  ENAME PIC  X(10).
              03  JOB PIC  X(9).
              03  MGR PIC 9(4).
              03  HIREDATE PIC  X(9).
              03  SAL PIC  X(6).
              03  COMM PIC  X(6).
              03  DEPTNO PIC  9(2).

       01  EMPOUTDATA.
           02  EMPOUT OCCURS 5 TIMES.
              03  EMPNO1 PIC  9(4) COMP.
              03  ENAME1 PIC  X(10).
              03  JOB1 PIC  X(9).
              03  MGR1 PIC 9(4).
              03  HIREDATE1 PIC  X(9).
              03  SAL1 PIC  X(6).
              03  COMM1 PIC  X(6).
              03  DEPTNO1 PIC  9(2).

           EXEC SQL END DECLARE SECTION END-EXEC.

       01  INSCNT PIC  9(3) COMP VALUE 25.
       01  FETCHCNT PIC  9(3) COMP VALUE 5.
       01  CNT PIC  9(4).
       01  CNT2 PIC  9(2).

       01  STRINGFIELDS.
           02 STR PIC X(18) VARYING.

           EXEC SQL INCLUDE SQLCA END-EXEC.

       PROCEDURE DIVISION.
       BEGIN-PGM.
           EXEC SQL WHENEVER SQLERROR DO PERFORM SQL-ERROR END-EXEC.

           PERFORM LOGON.

      * Fill the array elements to insert.
   PERFORM FILL-DATA VARYING CNT FROM 1 BY 1
           UNTIL CNT > INSCNT.

      * Inserting data using DB2 array insert syntax.
   DISPLAY "Inserting data using DB2 array insert syntax".
   EXEC SQL INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE,
            SAL, COMM, DEPTNO) VALUES (:EMPIN)
    FOR :INSCNT ROWS
   END-EXEC.

   EXEC SQL SELECT COUNT(*) INTO :CNT FROM EMP
            WHERE ENAME LIKE 'EMP_%'
   END-EXEC.
   DISPLAY "Number of rows successfully inserted into EMP "
           "table:", CNT.

   DISPLAY " ".
      * Declares scrollable cursor to fetch data.
           EXEC SQL DECLARE C1 SCROLL CURSOR FOR
                    SELECT EMPNO, ENAME, JOB, MGR, HIREDATE, SAL,
    COMM, DEPTNO
    FROM EMP
            WHERE ENAME LIKE 'EMP_%'
    ORDER BY EMPNO
   END-EXEC.

           EXEC SQL OPEN C1 END-EXEC.

   DISPLAY "Fetching data using DB2 array fetch syntax ".
         PERFORM FETCH-TAB.
       ENDFETCH-TAB.

   EXEC SQL CLOSE C1 END-EXEC.

           EXEC SQL ROLLBACK WORK RELEASE END-EXEC.
           STOP RUN.

       LOGON.
           MOVE "scott" TO USERNAME-ARR.
           MOVE 5 TO USERNAME-LEN.
           MOVE "tiger" TO PASSWD-ARR.
           MOVE 5 TO PASSWD-LEN.
           EXEC SQL
               CONNECT :USERNAME IDENTIFIED BY :PASSWD
           END-EXEC.

      * FILLS ARRAY TO INSERT INTO EMP TABLE
       FILL-DATA.
           MOVE CNT TO EMPNO(CNT).

   MOVE " " TO STR.
   STRING "EMP_", CNT INTO STR
   END-STRING.
   MOVE STR TO ENAME(CNT).

   MOVE " " TO STR.
   STRING "JOB_", CNT INTO STR
   END-STRING.
   MOVE STR TO JOB(CNT).

   MOVE 100 TO MGR(CNT).

   IF CNT > 30 THEN
       COMPUTE CNT2 = 30
   ELSE
       MOVE CNT TO CNT2
   END-IF

   MOVE " " TO STR.
   STRING CNT2, "-JAN-06" INTO STR
   END-STRING.
   MOVE STR TO HIREDATE(CNT).

   MOVE " " TO STR.
   STRING CNT2, "000" INTO STR
   END-STRING.
   MOVE STR TO SAL(CNT).

   MOVE 1000 TO COMM(CNT).

   MOVE 10 TO DEPTNO(CNT).

      * FETCHES DATA FROM EMP TABLE
       FETCH-TAB.
           EXEC SQL WHENEVER NOT FOUND GOTO ENDFETCH-TAB END-EXEC.
   DISPLAY "Fetch using FETCH FIRST ROWSET".
           EXEC SQL FETCH FIRST ROWSET FROM C1 FOR :FETCHCNT ROWS
            INTO :EMPOUT
   END-EXEC.
   PERFORM PRINTDATA.

   DISPLAY " ".
   DISPLAY "Fetch using FETCH NEXT ROWSET".
           EXEC SQL FETCH NEXT ROWSET FROM C1 FOR 5 ROWS
            INTO :EMPOUT END-EXEC.
   PERFORM PRINTDATA.

   DISPLAY " ".
   DISPLAY "Fetch using FETCH CURRENT ROWSET".
           EXEC SQL FETCH CURRENT ROWSET FROM C1 FOR :FETCHCNT ROWS
            INTO :EMPOUT
   END-EXEC.
   PERFORM PRINTDATA.

   DISPLAY " ".
   DISPLAY "Fetch using FETCH LAST ROWSET".
           EXEC SQL FETCH LAST ROWSET FROM C1 FOR :FETCHCNT ROWS
            INTO :EMPOUT
   END-EXEC.
   PERFORM PRINTDATA.

   DISPLAY " ".
   DISPLAY "Fetch using FETCH ROWSET STARTING AT ABSOLUTE".
   COMPUTE CNT = 4 * FETCHCNT.
           EXEC SQL FETCH ROWSET STARTING AT ABSOLUTE :CNT FROM C1
            FOR 5 ROWS INTO :EMPOUT
   END-EXEC.
   PERFORM PRINTDATA.

   DISPLAY " ".
   DISPLAY "Fetch using FETCH ROWSET STARTING AT RELATIVE".
           EXEC SQL FETCH ROWSET STARTING AT RELATIVE -3 FROM C1
            FOR :FETCHCNT ROWS INTO :EMPOUT
   END-EXEC.
   PERFORM PRINTDATA.

   DISPLAY " ".
   DISPLAY "Fetch using FETCH PRIOR ROWSET ".
           EXEC SQL FETCH PRIOR ROWSET FROM C1 FOR :FETCHCNT ROWS
            INTO :EMPOUT
   END-EXEC.
   PERFORM PRINTDATA.

      * Prints fetched data
       PRINTDATA.
   PERFORM VARYING CNT FROM 1 BY 1 UNTIL CNT > FETCHCNT
             DISPLAY "Empno=", EMPNO1(CNT), ", Ename=", ENAME1(CNT),
             ", Job=", JOB1(CNT), ", Mgr=", MGR1(CNT),
                     ", Hiredate=", HIREDATE1(CNT)
             DISPLAY "Sal=", SAL1(CNT), ", Comm=", COMM1(CNT),
             ", Deptno=", DEPTNO1(CNT)
           END-PERFORM.

      * HANDLES SQL ERROR CONDITIONS
       SQL-ERROR.
           EXEC SQL WHENEVER SQLERROR CONTINUE END-EXEC.
           DISPLAY " ".
           DISPLAY "ORACLE ERROR DETECTED:".
           DISPLAY " ".
           DISPLAY SQLERRMC.
           EXEC SQL ROLLBACK WORK RELEASE END-EXEC.
           STOP RUN.

暗黙的なバッファ済INSERTの使用

Pro*Cobolアプリケーションの開発者は、埋込みSQL文のホスト配列を参照して、パフォーマンスを向上させることができます。これにより、データベースへの1回のラウンドトリップでSQL文の配列を実行できます。配列の実行によってパフォーマンスが大幅に向上するにもかかわらず、ANSI規格ではないため、この機能を使用しない開発者もいます。たとえば、Oracle製品で配列の実行を使用するように記述されたアプリケーションは、IBMのプリコンパイラを使用してプリコンパイルすることはできません。

対処方法として、バッファ済INSERT文を使用すると、ANSI規格の埋込みSQL構文を保持しながらパフォーマンスを向上させることができます。

コマンドライン・オプション「max_row_insert」は、INSERT文の実行前にバッファする行の数を制御します。このオプションのデフォルトは0で、機能は無効化されています。この機能を有効化するには、0よりも大きい任意の数を指定します。

挿入バッファを有効化すると、プリコンパイラのランタイムが対応するカーソルにフラグを付け、次を実行します。

バッファ済INSERT文をフラッシュする新しい埋込みSQL文を実行している場合は、次を実行します。

標準のプリコンパイラのエラー・メカニズム(Pro*CobolのSQLCODE、SQLSTATEなど)を介してエラーがアプリケーションに通知されます。

「implicit_svpt」オプションは、新しくバッチ処理された挿入を開始する前に、暗黙的なセーブポイントを設定するかどうかを制御します。

バッファ済INSERT中に発生する可能性のあるエラーの一部を次に示します。

例7-2 表へのバッファ済の行の挿入

このプログラムでは、EMP表に行のLOOPCNT数を挿入します。loop counter=5の場合、このプログラムは無効なempnoの挿入を試行します。max_row_insertオプションを使用しないと、プログラムでは無効な行を除くすべての行が挿入されます。max_row_insertオプションをLOOPCNTに設定すると、最初の4行のみが挿入されます。

max_row_insertオプションを使用すると、間違った文が削除されるときに、プログラムは配列INSERTプログラムと同様に動作します。

      *****************************************************************
      * bufinsdemo:                                                   *
      *                                                               *
      * This program inserts LOOPCNT number of rows into EMP table.   *
      * At loop counter=5, this program attempts to insert an invalid *
      * empno. Without max_row_insert option, this program inserts    *
      * all rows except this invalid row. When max_row_insert option  *
      * is set to LOOPCNT, only the first 4 rows are inserted.        *
      *                                                               *
      * With max_row_insert option, when this errorneous statement    *
      * is removed, the performance of this program is similar to     *
      * having an array insert in this program.                       *
      *****************************************************************

       IDENTIFICATION DIVISION.
       PROGRAM-ID.  bufinsdemo.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       DATA DIVISION.
       WORKING-STORAGE SECTION.

      * EMBEDDED COBOL (file "BUFINSDEMO.PCO")

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.
       01  USERNAME          PIC X(10) VARYING.
       01  PASSWD            PIC X(10) VARYING.

       01  EMPIN.
           02  EMPNO PIC  9(6) COMP.
           02  ENAME PIC  X(10).
           02 JOB PIC  X(9).
           02  MGR PIC 9(4).
           02  HIREDATE PIC  X(9).
           02  SAL PIC  X(6).
           02  COMM PIC  X(6).
           02  DEPTNO PIC  9(2).

       01  EMPOUT.
           02  EMPNO1 PIC  9(4) COMP.
           02  ENAME1 PIC  X(10).
           02  JOB1 PIC  X(9).
           02  MGR1 PIC 9(4).
           02  HIREDATE1 PIC  X(9).
           02  SAL1 PIC  X(6).
           02  COMM1 PIC  X(6).
           02  DEPTNO1 PIC  9(2).

           EXEC SQL END DECLARE SECTION END-EXEC.

       01  LOOPCNT PIC  9(4) COMP VALUE 100.
       01  CNT PIC  9(4).
       01  CNT2 PIC  9(2).

       01  STRINGFIELDS.
           02 STR PIC X(18) VARYING.

           EXEC SQL INCLUDE SQLCA END-EXEC.

       PROCEDURE DIVISION.
       BEGIN-PGM.
           EXEC SQL WHENEVER SQLERROR DO PERFORM SQL-ERROR END-EXEC.

           PERFORM LOGON.

      * When max_row_insert option is set to LOOPCNT and when the errorneous
      * statement is removed, all the rows will be inserted into the database
      * in one stretch and hence maximum performance gain will be achieved.
   DISPLAY "Inserting ", LOOPCNT, " rows into EMP table".
   PERFORM INS-TAB VARYING CNT FROM 1 BY 1
           UNTIL CNT > LOOPCNT.

   EXEc SQL COMMIT END-EXEC.

   EXEC SQL SELECT COUNT(*) INTO :CNT FROM EMP
            WHERE ENAME LIKE 'EMP_%'
   END-EXEC.
   DISPLAY "Number of rows successfully inserted into EMP "
           "table:", CNT.

   DISPLAY " ".
           EXEC SQL DECLARE C1 CURSOR FOR
                    SELECT EMPNO, ENAME, JOB, MGR, HIREDATE, SAL,
    COMM, DEPTNO
    FROM EMP
            WHERE ENAME LIKE 'EMP_%'
    ORDER BY EMPNO
   END-EXEC.

           EXEC SQL OPEN C1 END-EXEC.

   DISPLAY "Fetching inserted rows from EMP table".
           PERFORM FETCH-TAB.
       ENDFETCH-TAB.

   EXEC SQL CLOSE C1 END-EXEC.

   EXEC SQL DELETE FROM EMP WHERE EMPNO < 1000 END-EXEC.

           EXEC SQL COMMIT WORK RELEASE END-EXEC.
           STOP RUN.

       LOGON.
           MOVE "scott" TO USERNAME-ARR.
           MOVE 5 TO USERNAME-LEN.
           MOVE "tiger" TO PASSWD-ARR.
           MOVE 5 TO PASSWD-LEN.
           EXEC SQL
               CONNECT :USERNAME IDENTIFIED BY :PASSWD
           END-EXEC.

      * INSERTS DATA INTO EMP TABLE
       INS-TAB.
   IF CNT = 5 THEN
     MOVE 10000 TO EMPNO
   ELSE
             MOVE CNT TO EMPNO
   END-IF

   MOVE " " TO STR.
   STRING "EMP_", CNT INTO STR
   END-STRING.
   MOVE STR TO ENAME.

   MOVE " " TO STR.
   STRING "JOB_", CNT INTO STR
   END-STRING.
   MOVE STR TO JOB.

   MOVE 100 TO MGR.

   IF CNT > 30 THEN
       COMPUTE CNT2 = 30
   ELSE
       MOVE CNT TO CNT2
   END-IF

   MOVE " " TO STR.
   STRING CNT2, "-JAN-06" INTO STR
   END-STRING.
   MOVE STR TO HIREDATE.

   MOVE " " TO STR.
   STRING CNT2, "000" INTO STR
   END-STRING.
   MOVE STR TO SAL.

   MOVE 1000 TO COMM.

   MOVE 10 TO DEPTNO.

   EXEC SQL INSERT INTO EMP (EMPNO, ENAME, JOB, MGR, HIREDATE,
            SAL, COMM, DEPTNO) VALUES (:EMPIN)
   END-EXEC.

      * FETCHES DATA FROM EMP TABLE
       FETCH-TAB.
           EXEC SQL WHENEVER NOT FOUND GOTO ENDFETCH-TAB END-EXEC.
           EXEC SQL FETCH C1 INTO :EMPOUT END-EXEC.
           DISPLAY "Empno=", EMPNO1, ", Ename=", ENAME1,
 ", Job=", JOB1, ", Mgr=", MGR1,
                   ", Hiredate=", HIREDATE1.
           DISPLAY "Sal=", SAL1, ", Comm=", COMM1, ", Deptno=", DEPTNO1.
 GO TO FETCH-TAB.

      * HANDLES SQL ERROR CONDITIONS
       SQL-ERROR.
           DISPLAY "ORACLE ERROR DETECTED:".
           DISPLAY SQLERRMC.