ヘッダーをスキップ
Pro*COBOL®プログラマーズ・ガイド
11gリリース2(11.2)
E50141-01
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次
索引へ移動
索引

前
 
次
 

11 Oracle動的SQL: 方法4

この章ではOracle動的SQL方法4を実装する方法を説明します。この方法では、ホスト変数を含む動的SQL文を受け取ったり、作成できます。含まれるホスト変数の個数は様々です。

新しいアプリケーションは、第10章「ANSI動的SQL」に示した最新のANSI SQL方法4を使用して開発してください。ANSI方法4では、Oracleのすべての型をサポートしています。しかし、古いOracle方法4では、カーソル変数、グループ項目の表、DML RETURNING句およびLOBをサポートしていません

内容は次のとおりです。


注意:

動的SQL方法1、2および3の詳細と方法4の概要は、第9章「Oracle動的SQL」を参照してください。

方法4の特殊要件

方法4の要件を学ぶ前に、選択リスト項目およびプレースホルダの用語を十分に理解してください。選択リスト項目とは、問合せ内でキーワードSELECTの後に続く列または式のことです。たとえば、次の動的問合せは3つの選択リスト項目を含んでいます。

SELECT ENAME, JOB, SAL + COMM FROM EMP WHERE DEPTNO = 20

プレースホルダとは、SQL文の中で実際のバインド変数用に場所を確保する、ダミーのバインド(入力)変数のことです。宣言する必要はなく、任意の名前を付けられます。バインド変数のプレースホルダは、SET、VALUESおよびWHERE句で多く使用されます。たとえば、次の動的SQL文にはそれぞれ2つのプレースホルダが記述されています。

INSERT INTO EMP (EMPNO, DEPTNO) VALUES (:E, :D)
DELETE FROM DEPT WHERE DEPTNO = :DNUM AND LOC = :DLOC

プレースホルダでは、表または列の名前は参照できません。

方法4の利点

方法1、2および3とは異なり、動的SQL方法4ではプログラムで次のことができます。

  • 選択リスト項目の数またはプレースホルダの数が不明な動的SQL文の受け入れまたは作成

  • OracleとCOBOL間のデータ型変換の明示的な制御

このような柔軟性をプログラムに加えるには、ランタイム・ライブラリに情報を追加する必要があります。

データベースに必要な情報

Pro*COBOLは、実行可能なすべての動的SQL文についてOracleのコールを作成します。データベースは、選択リスト項目もプレースホルダも組み込まれていない動的SQL文の実行には追加情報を必要としません。次のDELETE文がこのカテゴリに該当します。

*    Dynamic SQL statement...
     MOVE 'DELETE FROM EMP WHERE DEPTNO = 30' TO STMT.

しかし、ほとんどの動的SQL文には選択リスト項目またはバインド変数のプレースホルダが組み込まれています。次のUPDATE文はその一例です。

*    Dynamic SQL statement with place-holders...
     MOVE 'UPDATE EMP SET COMM = :C WHERE EMPNO = :E' TO STMT.

選択リスト項目またはバインド変数のプレースホルダ(あるいはその両方)が組み込まれている動的SQL文を実行する場合、データベースは出力値または入力値を格納するプログラム変数についての情報を必要とします。データベースでは、特に次の情報が必要です。

  • 選択リスト項目およびバインド変数の数

  • 各選択リスト項目および各バインド変数の長さ

  • 各選択リスト項目および各バインド変数のデータ型

  • 選択リスト項目の値を格納する各出力変数のメモリー・アドレスおよび各バインド変数のアドレス

たとえば、選択リスト項目の値を書き込むには、データベースは対応する出力変数のアドレスが必要です。

情報の格納位置

選択リスト項目またはバインド変数のプレースホルダに関してデータベースが必要とする情報は、(その値を除いて)すべてSQL記述子領域(SQLDA)と呼ばれるプログラム・データ構造に格納されます。

選択リスト項目の記述は選択SQLDAに格納され、バインド変数のプレースホルダの記述はバインドSQLDAに格納されます。

選択リスト項目の値は出力バッファに格納され、バインド変数の値は入力バッファに格納されます。これらのデータ・バッファのアドレスをライブラリ・ルーチンSQLADRを使用して選択SQLDAまたはバインドSQLDAに格納すると、データベースは出力値の書込み先や入力値の読取り元を認識できます。

値はどのようにしてこれらのデータ変数に格納されるのでしょうか。FETCHはカーソルを使用して出力値を生成します。入力値は、通常はユーザーが対話形式で入力した情報をもとに、プログラムによって代入されます。

情報の取得方法

データベースが必要とする情報を取得するには、DESCRIBE文を使用します。DESCRIBE SELECT LIST文は、各選択リスト項目を調べて、その名前、データ型、制約、長さ、位取りおよび精度を確認した後、それらの情報を選択SQLDAに格納します。たとえば、格納された情報は、後で印刷出力の列見出しとして選択リスト名を使用するときなどに使用できます。DESCRIBEでは、選択リスト項目の総数もSQLDAに格納されます。

DESCRIBE BIND VARIABLES文は、各プレースホルダを調べて、その名前および長さを確認した後、それらの情報を入力バッファおよびバインドSQLDAに格納します。格納された情報は、後でプレースホルダ名を使用してバインド変数の値の入力をユーザーに求めるときなどに使用できます。

SQL記述子領域(SQLDA)の理解

この項ではSQLDAのデータ構造を詳しく説明します。SQLDAの宣言方法、格納されている変数、初期化の方法およびプログラム内での使用方法を理解できます。

SQLDAの目的

方法4は、選択リスト項目の数またはバインド変数のプレースホルダの数が不明な動的SQL文の処理に必要とされます。このような動的SQL文を処理するには、プログラムでSQLDA (記述子とも呼ばれます)を明示的に宣言する必要があります。個々の記述子は、プログラム内のグループ項目に対応します。

選択記述子には、選択リスト項目の記述、および選択リスト項目の名前および値を格納する出力バッファのアドレスが格納されます。


注意:

選択リスト項目の名前には、列名、列の別名またはSAL+COMMなどの式を表すテキストが使用できます。

バインド記述子にはバインド変数と標識変数の記述、およびバインド変数と標識変数の名前と値が格納されている入力バッファのアドレスを格納します。

記述子変数の中には、値ではなくアドレスを格納するものがあります。このため、値を保持するためのデータ・バッファを宣言する必要があります。必要な入力バッファおよび出力バッファのサイズは自由に設定できます。COBOLではポインタはサポートされていないので、入力バッファおよび出力バッファのアドレスを取得するにはライブラリ・サブルーチンSQLADRを使用する必要があります。SQLADRのコール方法については、「SQLADRの使用」を参照してください。

複数のSQLDA

プログラムにアクティブな動的SQL文が2つ以上ある場合は、それぞれの文が専用のSQLDAを持つ必要があります。別の名前で任意の数のSQLDAを宣言できます。たとえば、同時にオープンされている3つのカーソルからFETCHするために、SELDSC1、SELDSC2およびSELDSC3の名前の3つの選択SQLDAを宣言できます。ただし、非並行のカーソルではSQLDAを再利用できます。

SQLDAの宣言

選択およびバインドSQLDAを宣言するには、図11-1に示す選択およびバインドSQLDAのサンプルを使用して、プログラムに記述する方法があります。配列の大きさは、必要に応じて変更できます。


注意:

バイトスワップ・プラットフォームの場合は、SQLDAの宣言時にCOMPのかわりにCOMP5を使用します。

図11-1 サンプルPro*COBOL SQLDA記述子およびデータ・バッファの例(32ビット)

サンプル記述子およびバッファ
「図11-1 サンプルPro*COBOL SQLDA記述子およびデータ・バッファの例(32ビット)」の説明


注意:

64ビット・プラットフォームの場合は、SQLDAの宣言時に、PIC S9(9)のかわりにPIC S9(18)を使用します。

次に示すように、SQLDAを(たとえばSELDSCおよびBNDDSCの名前の)ファイルに格納して、INCLUDE文を使用してそのファイルをプログラムにコピーできます。

     EXEC SQL INCLUDE SELDSC END-EXEC.
     EXEC SQL INCLUDE BNDDSC END-EXEC.

図11-2は、変数がSQLADRコール、DESCRIBEコマンド、FETCHコマンドまたはプログラム代入のどれによって設定されるかを示しています。

図11-2 変数の設定方法

変数の設定
「図11-2 変数の設定方法」の説明

SQLDA変数

この項では、SQLDA内の各変数の用途および使用方法を説明します。

SQLDNUM

この変数では、DESCRIBEに含めることができる選択リスト項目またはプレースホルダの最大数を指定します。したがって、SQLDNUMによって記述子表の要素の数(ディメンション)が決まります。

DESCRIBEコマンドを発行する前に、この変数を記述子表のサイズに設定する必要があります。また、DESCRIBEの実行後に、この変数をDESCRIBE内の実際の変数の数(これはSQLDFNDに格納されます)に再設定する必要があります。

SQLDFND

SQLDFND変数は、DESCRIBEコマンドで実際に検出された選択リスト項目またはプレースホルダの数です。

SQLDFNDはDESCRIBEによって設定されます。SQLDFNDが負の値の場合は、DESCRIBEコマンドが検出した選択リスト項目またはプレースホルダの数が、記述子のサイズに対して多すぎることを意味します。たとえば、SQLDNUMを10に設定した場合にDESCRIBEで11個の選択リスト項目またはプレースホルダが検索されると、SQLDFNDは-11に設定されます。この場合、記述子を再び割り当てないかぎりSQL文の処理はできません。

DESCRIBEの後で、SQLDNUMをSQLDFNDの値に設定してください。

SELDV | BNDDV

SELDV | BNDDV表は、選択リストまたはバインド変数の値を格納するデータ・バッファのアドレスの表です。

SELDVおよびBNDDVの要素は、SQLADRを使用して設定する必要があります。

選択記述子の場合

次の文を実行するとします。

     EXEC SQL FETCH ... USING DESCRIPTOR ...

この文は、FETCHされた選択リスト変数を、SELDV(1)からSELDV(SQLDNUM)でアドレス指定されたデータ・バッファに格納するようにデータベースに指示します。したがって、データベースはJ番目の選択リスト値をSEL-DV(J)に格納します。

バインド記述子の場合

バインド記述子は、OPENコマンドを発行する前に設定する必要があります。次の文を実行するとします。

     EXEC SQL OPEN ... USING DESCRIPTOR ...

この文は、BNDDV(1)からBNDDV(SQLDNUM)でアドレス指定されたバインド変数の値を使用して動的SQL文を実行するように、Oracleに指示します。(通常、値はユーザーによって入力されます。)データベースは、J番目のバインド変数値をBND-DV(J)から検索します。

SELDFMT | BNDDFMT

SELDFMT | BNDDFMT表は、選択リストまたはバインド変数の変換フォーマット文字列の値を格納するデータ・バッファのアドレスの表です。現在は、COBOLのパック10進数にのみ使用できます。変換文字列の書式はPP.+SSまたはPP.-SSです。PPは精度、SSは位取りです。精度および位取りの定義については、「精度および位取りの抽出」の項を参照してください。

フォーマット文字列の使用はオプションです。J番目の選択リスト項目またはバインド変数に変換形式を使用する場合は、SQLADRを使用してSELDFMT(J)またはBNDDFMT(J)を設定し、パック10進形式(07.+02など)をSEL-DFMTまたはBND-DFMTに格納します。変換形式を使用しない場合は、SELDFMT(J)またはBNDDFMT(J)を0 (ゼロ)に設定してください。

SELDVLN | BNDDVLN

SELDVLN | BNDDVLNは、データ・バッファに格納される選択リスト変数またはバインド変数値の長さの表です。

選択記述子の場合

この表は、DESCRIBE SELECT LISTによって、各選択リスト項目に期待される最大値に設定されます。しかし、FETCHコマンドを発行する前に長さを再設定する場合も考えられます。FETCHでは最大でn文字が戻されます(nは、FETCHコマンドを実行する前のSELDVLN(J)の値です)。

長さの形式はデータ型によって異なります。CHAR型の選択リスト項目の場合は、DESCRIBE SELECT LISTはSELDVLN(J)をその選択リスト項目の最大長(バイト数)に設定します。NUMBER型の選択リスト項目については、位取りおよび精度が変数の下位バイトおよびその次の上位側バイトにそれぞれ戻されます。ライブラリ・ルーチンSQLPRCを使用して、SELDVLNから精度および位取りを抽出できます。「精度および位取りの抽出」を参照してください。

FETCHを実行する前に、SELDVLN(J)を必要なデータ・バッファの長さに再設定する必要があります。たとえば、NUMBER型の数値をCOBOLの文字列に強制変換するときには、SELDVLN(J)を、その数値の精度に符号および小数点のために2を加えた長さに設定してください。また、NUMBER型の数値をCOBOLの浮動小数点数に強制変換するときには、SELDVLN(J)を、使用しているシステムでの適切な浮動小数点型の長さに設定してください。

強制変換するデータ型の長さの詳細は、「データの交換」を参照してください。

バインド記述子の場合

OPENコマンドを発行する前に、バインド記述子の長さを設定する必要があります。たとえば、次の文を使用して、ユーザーが入力したバインド変数文字列の長さを設定できます。

 PROCEDURE DIVISION. 
     ... 
     PERFORM GET-INPUT-VAR 
         VARYING J FROM 1 BY 1 UNTIL J > SQLDNUM IN BNDDSC. 
     ... 
 GET-INPUT-VAR. 
     DISPLAY "Enter value of ", BND-DH-VNAME(J). 
     ACCEPT INPUT-STRING. 
     UNSTRING INPUT-STRING DELIMITED BY "  " 
         INTO BND-DV(J) COUNT IN BNDDVLN(J). 

Oracleは、SELDV(J)またはBNDDV(J)に格納されているアドレスを使用して間接的にデータ・バッファにアクセスするため、データ・バッファ内の値の長さは認識しません。J番目の選択リスト値またはバインド変数値に対してOracleが使用する長さを変更する場合は、SELDVLN(J)またはBNDDVLN(J)を必要な長さに再設定してください。入力バッファまたは出力バッファにはそれぞれ異なる長さを指定できます。

SELDFMTL | BNDDFMTL

これは、選択リストまたはバインド変数の変換フォーマット文字列の長さの表です。現在は、COBOLのパック10進数にのみ使用できます。

フォーマット文字列の使用はオプションです。J番目の選択リスト項目に変換形式を使用する場合は、FETCHの前に、SELDFMTL(J)をSEL-DFMTに格納されているパック10進形式の長さに設定します。J番目のバインド変数に変換形式を使用する場合は、OPENの前に、BNDDFMTL(J)をBND-DFMTに格納されているパック10進形式の長さに設定します。変換形式を使用しない場合は、SELDFMTL(J)またはBNDDFMTL(J)を0(ゼロ)に設定してください。

SELDFMTL(J)の値が0 (ゼロ)の場合は、SELDFMT(J)は使用されません。同様に、BNDDFMTL(J)の値が0 (ゼロ)の場合は、BNDDFMT(J)は使用されません。

SELDVTYP | BNDDVTYP

SELDVTYP | BNDDVTYP表は、選択リスト値またはバインド変数値のデータ型コードの表です。データ型コードによって、SELDVの要素でアドレス指定されたデータ・バッファに格納されたときに、Oracleのデータがどのように変換されるかが決定されます。データ型記述子表の詳細は、「データの変換」を参照してください。

選択記述子の場合

DESCRIBE SELECT LISTにより、この表は、選択リスト項目の内部データ型(VARCHAR2、CHAR、NUMBER、DATEなど)に設定されます。

データ型の内部形式は処理が難しいため、FETCHを実行する前にデータ型を再設定することをお薦めします。表示用には、選択リスト値のデータ型をVARCHAR2に強制変換するのが一般的です。計算用には、数値をOracleからCOBOL書式に強制変換できます。「データ型の強制変換」を参照してください。

SELDVTYP(J)の上位ビットは、J番目の選択リスト列のNULL/NOT NULL状態を示します。OPENコマンドまたはFETCHコマンドを発行する前に、常にこのビットを消去する必要があります。ライブラリ・ルーチンSQLNULを使用して、データ型コードを取り出し、NULL/NOT NULLビットを消去します。詳細は、「NULLまたはNot NULLデータ型の処理」を参照してください。

NUMBER内部データ型は、SELDV(J)でアドレス指定されたCOBOLデータ・バッファの外部データ型と互換性のある外部データ型に変更することをお薦めします。

バインド記述子の場合

DESCRIBE BIND VARIABLESは、この表を0 (ゼロ)に設定します。OPENコマンドを発行する前に、このデータ型の表を再設定する必要があります。コードは、BNDDV(J)でアドレス指定されたバッファの外部(COBOL)データ型を表します。多くの場合、バインド変数値は文字列に格納されるので、データ型表の要素は1 (VARCHAR2データ型コード)に設定されます。

J番目の選択リスト値またはバインド変数値のデータ型を変更するには、SELDVTYP(J)またはBNDDVTYP(J)を希望するデータ型に再設定してください。

SELDI | BNDDI

SELDI | BNDDI表は、標識変数の値を格納するデータ・バッファのアドレスの表です。SELDIまたはBNDDIの要素は、SQLADRを使用して設定する必要があります。

選択記述子の場合

この表は、FETCHコマンドを発行する前に設定する必要があります。次の文を実行するとします。

     EXEC SQL FETCH ... USING DESCRIPTOR ... 

戻された選択リストのJ番目の値がNULLの場合は、SELDI(J)でアドレス指定されたバッファが-1に設定されます。それ以外の場合は、(値がNULLでない) 0または(値が切り捨てられている)正の整数に設定されます。

バインド記述子の場合

OPENコマンドを実行する前にこの表を初期化し、対応する標識変数を設定する必要があります。次の文を実行するとします。

     EXEC SQL OPEN ... USING DESCRIPTOR ...

BNDDI(J)でアドレス指定されたバッファによって、J番目のバインド変数がNULLかどうかがわかります。標識変数の値が-1の場合、対応するバインド変数はNULLです。

SELDH-VNAME | BNDDH-VNAME

SELDH-VNAME | BNDDH-VNAME表は、動的SQL文に表示される選択リストまたはプレースホルダの名前を格納するデータ・バッファのアドレスの表です。SELDH-VNAMEまたはBNDDH-VNAMEの要素は、DESCRIBEコマンドを発行する前にSQLADRを使用して設定する必要があります。

DESCRIBEは、J番目の選択リスト項目またはプレースホルダの名前をSELDH-VNAME(J)またはBNDDH-VNAME(J)でアドレス指定されたデータ・バッファに格納するように、Oracleに指示します。Oracleは、J番目の選択リストまたはプレースホルダの名前をSEL-DH-VNAME(J)またはBND-DH-VNAME(J)に格納します。


注意:

SELDH-VNAME | BNDDH-VNAME表は列名のみを格納します。SQL文中で指定した場合でも、表修飾子付きの列名は格納しません。たとえば、SQL文select a.owner from all_tablesで選択リストの記述を行うと、not a.ownerではなくownerが戻されます。必要に応じて列別名を使用して、選択リストの列が正しく識別されるようにします。

SELDH-MAX-VNAMEL | BNDDH-MAX-VNAMEL

SELDH-MAX-VNAMEL | BNDDH-MAX-VNAMEL表は、選択リストまたはプレースホルダの名前を格納するデータ・バッファの最大長の表です。バッファはSELDH-VNAMEまたはBNDDH-VNAMEの要素によってアドレス指定されます。

SELDH-MAX-VNAMELまたはBNDDH-MAX-VNAMELの要素は、DESCRIBEコマンドを発行する前に設定する必要があります。選択リスト名のバッファまたはプレースホルダ名のバッファは、それぞれ長さが異なっていてもかまいません。

SELDH-CUR-VNAMEL | BNDDH-CUR-VNAMEL

SELDH-CUR-VNAMEL | BNDDH-CUR-VNAMEL表は、選択リストまたはプレースホルダの実際の長さの表です。DESCRIBEはこの表を、各選択リスト名またはプレースホルダ名の文字数に設定します。

SELDI-VNAME | BNDDI-VNAME

SELDI-VNAME | BNDDI-VNAME表は、標識変数の値を格納するデータ・バッファのアドレスの表です。

標識変数の値は、選択リスト項目およびバインド変数と対応付けできます。ただし標識変数の名前は、バインド変数のみに対応付けることができます。この表はバインド記述子でのみ使用できます。BNDDI-VNAMEの要素は、DESCRIBEコマンドを発行する前にSQLADRを使用して設定する必要があります。

DESCRIBE BIND VARIABLESは、標識変数名をBNDDI-VNAME(1)からBNDDI-VNAME(SQLDNUM)でアドレス指定されたデータ・バッファに格納するように、Oracleに指示します。Oracleは、J番目の標識変数名をBND-DI-VNAME(J)に格納します。

SELDI-MAX-VNAMEL | BNDDI-MAX-VNAMEL

SELDI-MAX-VNAMEL | BNDDI-MAX-VNAMEL表は、標識変数名を格納するデータ・バッファの最大長の表です。バッファは、SELDI-VNAMEまたはBNDDI-VNAMEの要素によってアドレス指定されます。

標識変数名はバインド変数にのみ対応付けできます。この表はバインド記述子でのみ使用できます。

要素BNDDI-MAX-VNAMEL(1)からBNDDI-MAX-VNAMEL(SQLDNUM)は、DESCRIBEコマンドの発行前に設定する必要があります。標識変数名のバッファは、それぞれ長さが異なっていてもかまいません。

SELDI-CUR-VNAMEL | BNDDI-CUR-VNAMEL

SELDI-CUR-VNAMEL | BNDDI-CUR-VNAMEL表は、標識変数名の実際の長さの表です。標識変数名はバインド変数にのみ対応付けできます。この表はバインド記述子でのみ使用できます。

DESCRIBE BIND VARIABLESは、この表を、各標識変数名の文字数に設定します。

SELDFCLP | BNDDFCLP

SELDFCLP | BNDDFCLPは、将来の使用のために確保されている表です。Oracleはグループ項目SELDSCまたはBNDDSCが特定のサイズであるとみなすため、この表が必要となります。現在はSELDFCLPおよびBNDDFCLPの要素を0 (ゼロ)に設定する必要があります。

SELDFCRCP | BNDDFCRCP

SELDFCRCP | BNDDFCRCPは、将来の使用のために確保されている表です。Oracleはグループ項目SELDSCまたはBNDDSCが特定のサイズであるとみなすため、この表が必要となります。現在はSELDFCRCPおよびBNDDFCRCPの要素を0 (ゼロ)に設定する必要があります。

前提知識

動的SQL方法4を実装するには次の処理についての知識が必要です。

SQLADRの使用

入力値および出力値を格納するデータ・バッファのアドレスを取得するには、ライブラリ・サブルーチンSQLADRをコールする必要があります。取得したアドレスをバインドSQLDAまたは選択SQLDAに格納すると、Oracleはバインド変数値の読取り元や選択リスト値の書込み先を認識できるようになります。

次の構文で、SQLADRをコールします。

     CALL "SQLADR" USING BUFFER, ADDRESS.

パラメータは次のとおりです。

BUFFER

選択リスト項目、バインド変数または標識変数の値または名前を格納するデータ・バッファ。

ADDRESS

データ・バッファのアドレスを戻す整変数。

SQLADRをコールすると、BUFFERのアドレスがADDRESSに格納されます。次の例では、SQLADRを使用して、選択記述子表SELDV、SELDH-VNAME、SELDIを初期化します。これらの表の要素によって、選択リスト値、選択リスト名、インジケータ値のデータ・バッファのアドレスが指定されます。

 PROCEDURE DIVISION.
     ... 
     PERFORM INIT-SELDSC 
         VARYING J FROM 1 BY 1 UNTIL J > SQLDNUM IN SELDSC.
     ... 
 INIT-SELDSC.
     CALL "SQLADR" USING SEL-DV(J), SELDV(J).
     CALL "SQLADR" USING SEL-DH-VNAME(J), SELDH-VNAME(J).
     CALL "SQLADR" USING SEL-DI(J), SELDI(J).

データの変換

この項では、データ型記述子表を詳しく説明します。データ型の同値化も動的SQL方法4も使用しないホスト・プログラムでは、内部データ型と外部データ型の間の変換はプリコンパイル時に決定されます。デフォルトでは、Pro*COBOLによって各ホスト変数に特定の外部データ型が割り当てられます。たとえば、型PIC S9(n) COMPのホスト変数にはINTEGER外部データ型がPro*COBOLにより割り当てられます。

しかし方法4を使用すると、データの変換および形式を制御できます。変換の指定は、データ型記述子表のデータ型コードを設定して行います。

内部データ型

内部データ型により、Oracleが疑似列値の表現のためにデータベース表に列値を格納する際の形式が指定されます。

DESCRIBE SELECT LISTコマンドを発行すると、Oracleは各選択リスト項目の内部データ型コードをSELDVTYP (データ型)記述子表に戻します。たとえば、J番目の選択リスト項目のデータ型コードはSELDVTYP(J)に戻されます。

表11-1は、内部のデータ型およびそのコードを示しています。

表11-1 内部のデータ型および関連コード

内部データ型 コード

VARCHAR2

NUMBER

LONG

ROWID

DATE

RAW

LONG RAW

CHAR

1

2

8

11

12

23

24

96


外部データ型

外部データ型には、入力ホスト変数および出力ホスト変数に値を格納するのに使用する形式を指定します。

DESCRIBE BIND VARIABLESコマンドは、データ型コードのBNDDVTYP表を0 (ゼロ)に設定します。このため、OPENコマンドを発行するにそれらのコードを再設定する必要があります。データ型コードは、様々なバインド変数にどの外部データ型が使用されるかをOracleに知らせます。J番目のバインド変数の外部データ型を指定するには、BNDDVTYP(J)を希望する外部データ型に再設定してください。

次の表は、外部データ型とそのコード、および対応するCOBOLデータ型を示したものです。

表11-2 Oracleの外部データ型および関連するCOBOLデータ型

名前 コード COBOLデータ型

VARCHAR2

1

PIC X(n)(MODE=ANSIの場合)

NUMBER

2

PIC X(n)

INTEGER

3

PIC S9(n) COMP

(SPARC Solaris 64ビット・プラットフォームでは、COMP5ではなく、COMPを使用します。)

PIC S9(n) COMP5

(バイトスワップ・プラットフォームの場合はCOMP5)

FLOAT

4

COMP-1

COMP-2

STRING(0)

5

PIC X(n)

VARNUM

6

PIC X(n)

DECIMAL

7

PIC S9(n)V9(n) COMP-3

LONG

8

PIC X(n)

VARCHAR(2)

9

PIC X(n) VARYING

PIC N(n) VARYING

ROWID

11

PIC X(n)

DATE

12

PIC X(n)

VARRAW(2)

15

PIC X(n)

RAW

23

PIC X(n)

LONG RAW

24

PIC X(n)

UNSIGNED

68

(サポートされていません)

DISPLAY

91

PIC S9...9V9...9 DISPLAY SIGN LEADING SEPARATE

PIC S9(n)V9(n) DISPLAY SIGN LEADING SEPARATE

LONG VARCHAR(2)

94

PIC X(n)

LONG VARRAW(2)

95

PIC X(n)

CHARF

96

PIC X(n)(MODE=ANSIの場合)

PIC N(n)(MODE=ANSIの場合)

CHARZ(0)

97

PIC X(n)

CURSOR

102

SQL-CURSOR


注意:

  1. EXEC SQL VAR文でのみ使用します。

  2. nバイトの長さフィールドを含みます。

データ型およびその書式の詳細は、「Oracle Database 11gのデータ型」を参照してください。

PL/SQLのデータ型

PL/SQLでは、各種の事前定義済スカラー・データ型および複合データ型を使用できます。スカラー型には内部コンポーネントはありません。複合型には、個々に操作できる内部コンポーネントがあります。表11-4は、事前定義済のPL/SQLのスカラー・データ型およびそれに相当する内部データ型を示しています。

表11-3 PL/SQLのデータ型とそれに相当する内部データ型

PL/SQLデータ型 Oracle内部データ型

VARCHAR

VARCHAR2

VARCHAR2

BINARY_INTEGER

DEC

DECIMAL

DOUBLE PRECISION

FLOAT

INT

INTEGER

NATURAL

NUMBER

NUMERIC

POSITIVE

REAL

SMALLINT

NUMBER

LONG

LONG

ROWID

ROWID

DATE

DATE

RAW

RAW

LONG RAW

LONG RAW

CHAR

CHARACTER

STRING

CHAR


データ型の強制変換

選択記述子の場合、DESCRIBE SELECT LISTはいずれの内部データ型も戻すことができます。文字データの場合など、ほとんどの場合内部データ型は適切な外部データ型と正確に対応しています。ただし、内部データ型には扱いにくい外部データ型にマップするものもあります。このため、SELDVTYP記述子表の要素の中には再設定が必要なものもあります。

たとえば、NUMBER値をFLOAT値(これはCOBOLのPIC S9(n)V9(n) COMP-1値に対応)に再設定します。Oracleは、内部データ型と外部データ型の間の必要な変換をFETCH時に行います。データ型の再設定は必ずDESCRIBE SELECT LISTの、FETCHのに行ってください。

バインド記述子の場合は、DESCRIBE BIND VARIABLESによってバインド変数のデータ型が戻されることはなく、バインド変数の数および名前のみ戻されます。したがって、データ型コードのBNDDVTYP表を明示的に設定して、各バインド変数の外部データ型をOracleに認識させる必要があります。Oracleは、内部データ型と外部データ型の間の必要な変換をOPEN時に行います。

SELDVTYPまたはBNDDVTYPの記述子表でデータ型コードをリセットするときに、データ型を強制変換します。たとえば、J番目の選択リスト値をVARCHAR2に強制変換するには、次の文を使用します。

*    Coerce select-list value to VARCHAR2. 
     MOVE 1 TO SELDVTYP(J). 

表示を目的としてNUMBER選択リスト値をVARCHAR2に強制変換する場合は、その値の精度および位取りのバイトを抽出し、その情報を使用して最大表示長を算出する必要があります。その後、FETCHを行う前にSELDVLN (長さ)記述子表の該当する要素を再設定して、使用するバッファの長さをOracleに認識させる必要があります。J番目の選択リスト値の長さを指定するには、SELDVLN(J)を必要な長さに設定します。

たとえば、DESCRIBE SELECT LISTでJ番目の選択リスト項目の型がNUMBERであるとわかった場合、戻り値をPIC S9(n)V9(n) COMP-1として宣言したCOBOL変数に格納するには、SELDVTYP(J)を4に設定し、SELDVLN(J)をシステムが定めるCOMP-1数値の長さに設定します。

例外

DESCRIBE SELECT LISTによって戻される内部データ型が、目的に合わない場合もあります。DATE型およびNUMBER型がその例です。DATE選択リスト項目をDESCRIBEすると、Oracleはデータ型コード12をSELDVTYP表に戻します。FETCHの前にコードを再設定しないかぎり、日付の値はその7バイト内部形式で戻されます。デフォルトの文字形式で日付を取得するには、データ型コードを12から1 (VARCHAR2)に変更して、SELDVLNの値を7から9に増やす必要があります。

同様に、NUMBER選択リスト項目をDESCRIBEすると、Oracleはデータ型コード2をSELDVTYP表に戻します。FETCHの前にコードを再設定しないかぎり、数値はその内部形式で戻されるため、求めている値と異なる可能性が高くなります。このような場合は、コードを2から1 (VARCHAR2)、3 (INTEGER)または4 (FLOAT)に変更するか、その他の適切なデータ型に変更してください。

精度および位取りの抽出

ライブラリ・サブルーチンSQLPRCにより、精度および位取りが抽出されます。このサブルーチンは通常、DESCRIBE SELECT LISTの後で使用され、その最初のパラメータはSELDVLN(J)です。次の構文で、SQLPRCをコールします。

     CALL "SQLPRC" USING LENGTH, PRECISION, SCALE.

パラメータは次のとおりです。

構文 説明
LENGTH NUMBER値の長さが格納される整変数。値の位取りおよび精度はそれぞれ、下位バイトおよびその上のバイトに格納されます。
PRECISION NUMBER値の精度を戻す整変数。精度とは有効桁数を指します。サイズが未指定のNUMBERが選択リスト項目によって参照される場合は、precisionの値は0 (ゼロ)に設定されます。この場合、サイズが未指定なので、最大精度の38とみなされます。
SCALE NUMBER値の位取りを戻す整変数。位取りには四捨五入する位置を指定します。たとえば、位取りが2の場合は最も近い100分の1の位に値が四捨五入され(3.456は3.46となります)、位取りが-3の場合は最も近い1000の位に値が四捨五入されます(3.456は3000となります)。

次の例に、SQLPRCを使用して、VARCHAR2に強制変換されるNUMBER値の最大表示長を算出する方法を示します。

 WORKING-STORAGE SECTION. 
 01  PRECISION       PIC S9(9) COMP. 
 01  SCALE           PIC S9(9) COMP. 
 01  DISPLAY-LENGTH  PIC S9(9) COMP. 
 01  MAX-LENGTH      PIC S9(9) COMP VALUE 80. 
     ... 
 PROCEDURE DIVISION. 
     ... 
     PERFORM ADJUST-LENGTH 
         VARYING J FROM 1 BY 1 UNTIL J > SQLDNUM IN SELDSC. 
 ADJUST-LENGTH. 
*    If datatype is NUMBER, extract precision and scale. 
     IF SELDVTYP(J) = 2 
         CALL "SQLPRC" USING SELDVLN(J), PRECISION, SCALE. 
     MOVE 0 TO DISPLAY-LENGTH. 
*    Precision is set to zero if the select-list item 
*    refers to a NUMBER of unspecified size.  We allow for 
*    a maximum precision of 10. 
     IF SELDVTYP(J) = 2 AND PRECISION = 0 
         MOVE 10 TO DISPLAY-LENGTH. 
*    Allow for possible decimal point and sign. 
     IF SELDVTYP(J) = 2 AND PRECISION > 0 
         ADD 2 TO PRECISION 
         MOVE PRECISION TO DISPLAY-LENGTH. 
     ... 

このサブルーチン・コールの最初のパラメータは、選択リストの長さの表のJ番目の要素になるので注意してください。

SQLPRCプロシージャ(これはSQLLIBランタイム・ライブラリで定義されています)により、特定のSQLデータ型については精度および位取りの値として0 (ゼロ)が戻されます。SQLPR2プロシージャは、この表に示すデータ型を除けば、同じ構文で同じバイナリ値を戻すという点でSQLPRCに似ています。

表11-4 SQLPR2プロシージャのデータ型の例外

SQLデータ型 2進数精度 2進数位取り

FLOAT

126

-127

FLOAT(n)

n(1から126)

-127

REAL

63

-127

DOUBLE PRECISION

126

-127


NULLまたはNOT NULLデータ型の処理

すべての選択リスト列(式は不可)について、DESCRIBE SELECT LISTは選択記述子のデータ型表にNULL/NOT NULLインジケータを戻します。J番目の選択リスト列にNOT NULL制約が指定されていると、SELDVTYP(J)データ型変数の上位ビットはオフにされます。それ以外の場合は上位ビットが設定されます。

NULLステータス・ビットがセットされている場合、OPEN文またはFETCH文でデータ型を使用する前に消去する必要があります。このビットはセットしないでください。

ライブラリ・ルーチンSQLNULを使用して、列にNULLデータ型を使用できるかどうかを調べたり、そのデータ型のNULLステータス・ビットを消去できます。次の構文で、SQLNULをコールします。

     CALL "SQLNUL" USING VALUE-TYPE, TYPE-CODE, NULL-STATUS.

パラメータは次のとおりです。

構文 説明
VALUE-TYPE 選択リスト列のデータ型コードを戻す2バイトの整変数。
TYPE-CODE 選択リスト列のデータ型コードを戻す2バイトの整変数。上位ビットは消去されます。
NULL-STATUS 選択リスト列のNULL状態を戻す整変数。1は列がNULLを許可し、0は許可しないことを意味します。

次の例は、SQLNULの使用方法を示したものです。

 WORKING-STORAGE SECTION.
     ... 
*    Declare variable for subroutine call.
     01  NULL-STATUS  PIC S9(9) COMP.
     ... 
 PROCEDURE DIVISION.
 MAIN.
     EXEC SQL WHENEVER SQLERROR GOTO SQL-ERROR END-EXEC.
     ...
     PERFORM HANDLE-NULLS
         VARYING J FROM 1 BY 1 UNTIL J > SQLDNUM IN SELDSC.
     ...
 HANDLE-NULLS.
*    Find out if column is NOT NULL, and clear high-order bit.
     CALL "SQLNUL" USING SELDVTYP(J), SELDVTYP(J), NULL-STATUS.
*    If NULL-STATUS = 1, NULLs are allowed.

このサブルーチン・コールの最初のパラメータと2番目のパラメータが同じことに注意してください。この2つのパラメータはそれぞれ、NULLステータス・ビットが消去される前と後のデータ型変数です。

基本手順

方法4は任意の動的SQL文に使用できます。「方法4でのホスト表の使用」の例では、入力ホスト変数および出力ホスト変数をどのように扱うかわかるように問合せが処理されています。

このサンプル・プログラムでは次の手順に従って動的問合せを処理します。

  1. 問合せテキストを保存するためのホスト文字列を宣言します。

  2. 選択記述子およびバインド記述子を宣言します。

  3. DESCRIBEできる選択リスト項目およびプレースホルダの最大数を設定します。

  4. 選択記述子およびバインド記述子を初期化します。

  5. 問合せテキストをホスト文字列に格納します。

  6. ホスト文字列から問合せをPREPAREします。

  7. 問合せ用のカーソルをDECLAREします。

  8. バインド記述子にバインド変数をDESCRIBEします。

  9. プレースホルダの数を、DESCRIBEで実際に検出された数に再設定します。

  10. DESCRIBEで検出されたバインド変数の値を取得します。

  11. バインド記述子を使用してカーソルをOPENします。

  12. 選択記述子に選択リストをDESCRIBEします。

  13. 選択リスト項目の最大数をDESCRIBEにより実際に検出された数に再設定します。

  14. 表示用にそれぞれの選択リスト項目の長さおよびデータ型を再設定します。

  15. 選択記述子を使用してデータベースからデータ・バッファに行をFETCH INTOします。

  16. FETCHにより戻された選択リストの値を処理します。

  17. FETCHする行がなくなった場合カーソルをCLOSEします。


注意:

動的SQL文が問合せではない場合、または個数がわかっている選択リスト項目またはプレースホルダを含む場合は、前述の一部のステップは必要ありません。

各手順の詳細

この項では、個々のステップを詳しく説明します。方法4の完全なサンプル・プログラムをこの章の最後に示します。方法4では、埋込みSQL文を次のような順序で使用します。

     EXEC SQL
         PREPARE <statement_name>
         FROM {:<host_string> | <string_literal>}
     END-EXEC.
     EXEC SQL 
         DECLARE <cursor_name> CURSOR FOR <statement_name>
     END-EXEC.
     EXEC SQL
         DESCRIBE BIND VARIABLES FOR <statement_name>
         INTO <bind_descriptor_name>
     END-EXEC.
     EXEC SQL
         OPEN <cursor_name> 
         [USING DESCRIPTOR <bind_descriptor_name>]
     END-EXEC.
     EXEC SQL
         DESCRIBE [SELECT LIST FOR] <statement_name>
         INTO <select_descriptor_name>
     END-EXEC.
     EXEC SQL
         FETCH <cursor_name> USING DESCRIPTOR <select_descriptor_name>
     END-EXEC.
     EXEC SQL
         CLOSE <cursor_name>
     END-EXEC.

動的問合せの選択リスト項目の数がわかっているときは、DESCRIBE SELECT LISTを省略するとともに次の方法3のFETCH文を使用できます。

EXEC SQL FETCH <cursor_name> INTO <host_variable_list> END-EXEC.

また、動的SQL文中のバインド変数のプレースホルダの数がわかっている場合は、DESCRIBE BIND VARIABLESを使用せずに、次に示す方法3のOPEN文を使用できます。

     EXEC SQL OPEN <cursor_name> [USING <host_variable_list>] END-EXEC.

次の項では、これらの文により、記述子を使用してホスト・プログラムで動的SQL文を受け入れ、それを処理する方法を説明します。


注意:

以降では、図を使用して説明します。図が複雑になるのを避けるため、記述子表の要素は3つまで、名前および値の最大長はそれぞれ5文字と10文字に制限しました。

ホスト文字列の宣言

プログラムには、動的SQL文のテキストを格納するためのホスト変数が必要です。このホスト変数(例ではSELECTSTMT)は、文字列として宣言する必要があります。

     EXEC SQL BEGIN DECLARE SECTION END-EXEC. 
     ... 
 01  SELECTSTMT  PIC X(120). 
     EXEC SQL END DECLARE SECTION END-EXEC. 

SQLDAの宣言

例では、問合せの選択リスト項目またはプレースホルダの数が不明な場合があるため、選択記述子およびバインド記述子を宣言する必要があります。SQLDAをハードコードするかわりに、次のように、INCLUDEを使用してSQLDAをプログラムにコピーします。

     EXEC SQL INCLUDE SELDSC END-EXEC. 
     EXEC SQL INCLUDE BNDDSC END-EXEC. 

参考のため、INCLUDEされるSELDSCの宣言を次に示します。

 WORKING-STORAGE SECTION. 
     ... 
 01  SELDSC. 
         05  SQLDNUM              PIC S9(9) COMP. 
         05  SQLDFND              PIC S9(9) COMP. 
         05  SELDVAR              OCCURS 3 TIMES. 
             10 SELDV             PIC S9(9) COMP. 
             10 SELDFMT           PIC S9(9) COMP. 
             10 SELDVLN           PIC S9(9) COMP. 
             10 SELDFMTL          PIC S9(4) COMP. 
             10 SELDVTYP          PIC S9(4) COMP. 
             10 SELDI             PIC S9(9) COMP. 
             10 SELDH-VNAME       PIC S9(9) COMP. 
             10 SELDH-MAX-VNAMEL  PIC S9(4) COMP. 
             10 SELDH-CUR-VNAMEL  PIC S9(4) COMP. 
             10 SELDI-VNAME       PIC S9(9) COMP. 
             10 SELDI-MAX-VNAMEL  PIC S9(4) COMP. 
             10 SELDI-CUR-VNAMEL  PIC S9(4) COMP. 
             10 SELDFCLP          PIC S9(9) COMP. 
             10 SELDFCRCP         PIC S9(9) COMP. 

 01  XSELDI. 
     05  SEL-DI        OCCURS 3 TIMES PIC S9(9) COMP. 
 01  XSELDIVNAME. 
         05  SEL-DI-VNAME  OCCURS 3 TIMES PIC X(5). 
 01  XSELDV. 
         05  SEL-DV        OCCURS 3 TIMES PIC X(10). 
 01  XSELDHVNAME. 
         05  SEL-DH-VNAME  OCCURS 3 TIMES PIC X(5).  

DESCRIBEへの最大数の設定

次に、記述できる選択リスト項目またはプレースホルダの最大数を設定します。

     MOVE 3 TO SQLDNUM IN SELDSC. 
     MOVE 3 TO SQLDNUM IN BNDDSC. 

記述子の初期化

初期化を必要とする記述子変数もあります。また、初期化にライブラリ・サブルーチンSQLADRが必要なものもあります。

例では、名前バッファの最大長がSELDH-MAX-VNAMEL表、BNDDH-MAX-VNAMELおよびBNDDI-MAX-VNAMEL表に格納され、SQLADRを使用して値バッファおよび名前バッファのアドレスが表SELDV、SELDI、BNDDV、BNDDI、SELDH-VNAME、BNDDH-VNAMEおよびBNDDI-VNAMEに格納されています。

 PROCEDURE DIVISION. 
     ... 
     PERFORM INIT-SELDSC 
         VARYING J FROM 1 BY 1 UNTIL J > SQLDNUM IN SELDSC. 
     PERFORM INIT-BNDDSC 
         VARYING J FROM 1 BY 1 UNTIL J > SQLDNUM IN BNDDSC. 
     ... 
 INIT-SELDSC. 
     MOVE SPACES TO SEL-DV(J). 
     MOVE SPACES TO SEL-DH-VNAME(J). 
     MOVE 5 TO SELDH-MAX-VNAMEL(J). 
     CALL "SQLADR" USING SEL-DV(J), SELDV(J). 
     CALL "SQLADR" USING SEL-DH-VNAME(J), SELDH-VNAME(J). 
     CALL "SQLADR" USING SEL-DI(J), SELDI(J). 
     ... 
 INIT-BNDDSC. 
     MOVE SPACES TO BND-DV(J). 
     MOVE SPACES TO BND-DH-VNAME(J). 
     MOVE SPACES TO BND-DI-VNAME(J). 
     MOVE 5 TO BNDDH-MAX-VNAMEL(J). 
     MOVE 5 TO BNDDI-MAX-VNAMEL(J). 
     CALL "SQLADR" USING BND-DV(J), BNDDV(J). 
     CALL "SQLADR" USING BND-DH-VNAME(J), BNDDH-VNAME(J). 
     CALL "SQLADR" USING BND-DI(J), BNDDI(J). 
     CALL "SQLADR" USING BND-DI-VNAME(J), BNDDI-VNAME(J).
     ...

図11-3図11-4に、結果として得られる記述子を示します。

図11-3 初期化した選択記述子

初期化した選択記述子
「図11-3 初期化した選択記述子」の説明

図11-4 初期化したバインド記述子

初期化したバインド記述子
「図11-4 初期化したバインド記述子」の説明

ホスト文字列への問合せテキストの格納

次に、ユーザーにSQL文の入力を要求し、入力された文字列をSELECTSTMTに格納します。

     DISPLAY "Enter a SELECT statement: " WITH NO ADVANCING. 
     ACCEPT SELECTSTMT. 

このときユーザーが次の文字列を入力したと仮定します。

     SELECT ENAME, EMPNO, COMM FROM EMP WHERE COMM < :BONUS

ホスト文字列からの問合せのPREPARE

PREPAREは、このSQL文を解析して名前を指定します。例では、PREPAREでホスト文字列SELECTSTMTを解析し、SQLSTMTの名前を付けます。

     EXEC SQL PREPARE SQLSTMT FROM :SELECTSTMT END-EXEC. 

カーソルの宣言

DECLARE CURSORは名前を指定し、特定のSELECT文に対応付けることにより、カーソルを定義します。

静的問合せ用のカーソルを宣言するには次の構文を使用します。

     EXEC SQL DECLARE cursor_name CURSOR FOR SELECT ... 

動的問合せ用にカーソルを宣言するには、PREPAREにより動的問合せに指定された文の名前で静的問合せを置換します。例では、次に示すように、DECLARE CURSORはEMPCURSORの名前のカーソルを定義し、それをSQLSTMTと対応付けます。

     EXEC SQL DECLARE EMPCURSOR CURSOR FOR SQLSTMT END-EXEC.

注意:

問合せのみでなく、すべての動的SQL文にカーソルを宣言する必要があります。また、問合せ以外の場合も、カーソルのOPENにより動的SQL文を実行します。

バインド変数のDESCRIBE

DESCRIBE BIND VARIABLESは、バインド変数の記述をバインド記述子に格納します。例では、DESCRIBEでBNDDSCを準備します。

     EXEC SQL 
         DESCRIBE BIND VARIABLES FOR SQLSTMT 
         INTO BNDDSC 
     END-EXEC. 

BNDDSCの前にはコロンを付けないでください

DESCRIBE BIND VARIABLES文はPREPARE文の後で、かつOPEN文の前に指定する必要があります。

図11-5に、DESCRIBE実行後のバインド記述子の例を示します。DESCRIBEにより、処理対象のSQL文で検出されたプレースホルダの実際の数に、SQLDFNDが設定されていることに注意してください。

図11-5 DESCRIBE実行後のバインド記述子

DESCRIBE実行後のバインド
「図11-5 DESCRIBE実行後のバインド」の説明

プレースホルダの数の再設定

次に、プレースホルダの最大数を、DESCRIBEで実際に検出された数に再設定する必要があります。

     IF SQLDFND IN BNDDSC < 0 
         DISPLAY "Too many bind variables" 
         GOTO ROLL-BACK 
     ELSE 
         MOVE SQLDFND IN BNDDSC TO SQLDNUM IN BNDDSC
     END-IF. 

バインド変数の値の取得

プログラムはSQL文中のバインド変数の値を取得する必要があります。値はどのように取得してもかまいません。たとえば値をハードコードしたり、ファイルから読み込むことや対話形式で入力することもできます。

例では、問合せのWHERE句のプレースホルダBONUSに置き換わるバインド変数に値を代入する必要があります。次のように、ユーザーに値の入力を求め、入力された値を処理します。

 PROCEDURE DIVISION. 
     ... 
     PERFORM GET-INPUT-VAR 
         VARYING J FROM 1 BY 1 UNTIL J > SQLDNUM IN BNDDSC. 
     ... 
 GET-INPUT-VAR. 
     ... 
*    Replace the 0 DESCRIBEd into the datatype table 
*    with a 1 to avoid an "invalid datatype" Oracle error. 
     MOVE 1 TO BNDDVTYP(J). 
*    Get value of bind variable. 
     DISPLAY "Enter value of ", BND-DH-VNAME(J). 
     ACCEPT INPUT-STRING. 
     UNSTRING INPUT-STRING DELIMITED BY "  " 
         INTO BND-DV(J) COUNT IN BNDDVLN(J). 

ここでは、ユーザーがBONUSの値として625を入力したとして、次の表に結果として得られるバインド記述子を示します。

図11-6 値を割り当てた後のバインド記述子

割り当て後
「図11-6 値を割り当てた後のバインド記述子」の説明

カーソルのOPEN

動的問合せのOPEN文は、カーソルがバインド記述子に対応付けられることを除けば、静的問合せのOPEN文と同じです。実行時に決定され、バインド記述子表の要素でアドレス指定されたバッファに格納された値を使用して、SQL文を評価します。問合せの場合は、アクティブ・セットの識別にも同じ値を使用します。

例では、OPENがEMPCURSORをBNDDSCに対応付けています。

     EXEC SQL 
         OPEN EMPCUR USING DESCRIPTOR BNDDSC 
     END-EXEC. 

BNDDSCの前にはコロンを付けないでください

OPENはSQL文を実行します。問合せのときは、OPENはアクティブ・セットを決定するとともにカーソルを先頭行に位置づけます。

選択リストのDESCRIBE

動的SQL文が問合せのときは、DESCRIBE SELECT LIST文はOPEN文の後で、かつFETCH文の前に指定する必要があります。

DESCRIBE SELECT LISTは、選択リスト項目の記述を選択記述子に格納します。例では、DESCRIBEによってSELDSCを準備しています。

     EXEC SQL 
         DESCRIBE SELECT LIST FOR SQLSTMT INTO SELDSC 
     END-EXEC. 

DESCRIBEは、データ・ディクショナリにアクセスして、各選択リスト値の長さおよびデータ型を設定します。

図11-7に、DESCRIBE実行後の選択記述子の例を示します。DESCRIBEにより、問合せの選択リストで実際に検出された項目の数に、SQLDFNDが設定されていることに注意してください。SQL文が問合せではない場合は、SQLDFNDは0 (ゼロ)に設定されます。また、NUMBER型の長さはまだ使用できないことに注意してください。NUMBERとして定義された列については、ライブラリ・サブルーチンSQLPRCを使用して精度および位取りを抽出する必要があります。「データ型の強制変換」を参照してください。

図11-7 DESCR実行後の選択記述子

DESCR実行後の選択
「図11-7 DESCR実行後の選択記述子」の説明

選択リスト項目の最大数の再設定

次に選択リスト項目の最大数を、DESCRIBEにより実際に検出された数に再設定する必要があります。

     MOVE SQLDFND IN SELDSC TO SQLDNUM IN SELDSC. 

各選択リスト項目の長さおよびデータ型の再設定

例では、選択リストの値をフェッチする前に、長さおよびデータ型の表の要素の一部を表示するために再設定します。

 PROCEDURE DIVISION. 
     ... 
     PERFORM COERCE-COLUMN-TYPE 
         VARYING J FROM 1 BY 1 UNTIL J > SQLDNUM IN SELDSC. 
     ... 
 COERCE-COLUMN-TYPE. 
*    Clear NULL bit. 
     CALL "SQLNUL" USING SELDVTYP(J), SELDVTYP(J), NULL-STATUS. 

*    If datatype is DATE, lengthen to 9 characters. 
     IF SELDVTYP(J) = 12 
         MOVE 9 TO SELDVLN(J). 

*    If datatype is NUMBER, extract precision and scale. 
     MOVE 0 TO DISPLAY-LENGTH. 
     IF SELDVTYP(J) = 2 AND PRECISION = 0 
         MOVE 10 TO DISPLAY-LENGTH. 
     IF SELDVTYP(J) = 2 AND PRECISION > 0 
         ADD 2 TO PRECISION 
         MOVE PRECISION TO DISPLAY-LENGTH. 
     IF SELDVTYP(J) = 2 
         IF DISPLAY-LENGTH > MAX-LENGTH 
             DISPLAY "Column value too large for data buffer." 
             GO TO END-PROGRAM 
         ELSE 
             MOVE DISPLAY-LENGTH TO SELDVLN(J). 

*    Coerce datatypes to VARCHAR2. 
     MOVE 1 TO SELDVTYP(J). 

図11-8に、結果として得られる選択記述子を示します。NUMBERの長さが使用でき、すべてのデータ型がVARCHAR2になっていることに注意してください。符号と小数点を使用できるよう、DESCRIBEした長さ4および7をそれぞれ2ずつ増加させたため、SELDVLN(2)およびSELDVLN(3)の長さが6および9になっています。

図11-8 FETCH実行前の選択記述子

DESCR実行後の選択
「図11-8 FETCH実行前の選択記述子」の説明

アクティブ・セットからの行のFETCH

FETCHはアクティブ・セットから1行を戻し、データ・バッファに選択リストの値を格納してから、カーソルをアクティブ・セットの次の行に進めます。行がなくなると、FETCHは「データが見つかりません。」のエラー・コードをSQLCAのSQLCODE、SQLCODE変数、またはSQLSTATE変数に設定します。次の例では、FETCHはENAME列、EMPNO列、およびCOMM列からSELDSC列の値を戻します。

     EXEC SQL 
         FETCH EMPCURSOR USING DESCRIPTOR SELDSC 
     END-EXEC. 

図11-9に、FETCH実行後の選択記述子の例を示します。Oracleが、SELDVおよびSELDIの要素によってアドレス指定されたデータ・バッファに、選択リストおよびインジケータの値を格納していることに注意してください。

データ型1の出力バッファの場合、OracleはSELDVLNに格納されている長さを使用して、CHARデータまたはVARCHAR2データは左揃えにし、NUMBERデータは右揃えにします。

MARTINは、EMP表のVARCHAR2(10)列から取り出されました。Oracleは、SELDVLN(1)に格納された長さを使用して、この値を10バイトのフィールドに左揃えにして入れ、バッファの残りの部分を埋めます。

値7654はNUMBER(4)列から取り出され、7654に強制変換されています。しかし、SELDVLN(2)の長さが2増加して符号および小数点を使用できるようになっているため、Oracleでは6バイトのフィールドで値が右揃えになります。

値482.50はNUMBER(7,2)列から取り出され、482.50に強制変換されています。ここでも、SELDVLN(3)の長さが2増加しているため、Oracleでは9バイトのフィールドで値が右揃えになります。

選択リストの値の取得および処理

FETCHの後、プログラムはFETCHで戻された選択リストの値を処理できます。例では、列ENAME、EMPNOおよびCOMMの値が処理されます。

カーソルのCLOSE

CLOSEはカーソルを使用禁止にします。例では、CLOSEによってEMPCURSORが使用禁止になります。

     EXEC SQL CLOSE EMPCURSOR END-EXEC

図11-9 FETCH実行後の選択記述子

FETCH実行後の選択
「図11-9 FETCH実行後の選択記述子」の説明

方法4でのホスト表の使用

方法4で入力ホスト表または出力ホスト表を使用するには、オプションのFOR句を使用してホスト表のサイズをOracleに認識させる必要があります。FOR句の詳細は、第7章「ホスト表」を参照してください。

J番目の選択リスト項目またはバインド変数の記述子エントリを設定します。ただし、SELDVLN(J)またはBNDDVLN(J)は、単一のデータ・バッファをアドレス指定するのではなく、データ・バッファの表をアドレス指定します。EXECUTE文またはFETCH文のいずれか適切な方にFOR句を指定して、処理対象の表要素の数をOracleに通知する必要があります。

Oracleがホスト表のサイズを認識する方法は他にないため、このステップは必須です。

次の例では、2つの入力ホスト表を使用して、EMPNOおよびDEPTNOの8組の値をEMP表に挿入します。方法4では、問合せ以外のSQL文に対してEXECUTEを使用できるので注意してください。

 IDENTIFICATION DIVISION. 
 PROGRAM-ID. DYN4INS. 
 ENVIRONMENT DIVISION. 
 DATA DIVISION. 
 WORKING-STORAGE SECTION. 
 01  BNDDSC. 
     02  SQLDNUM              PIC S9(9) COMP VALUE 2. 
     02  SQLDFND              PIC S9(9) COMP. 
     02  BNDDVAR              OCCURS 2 TIMES. 
         03 BNDDV             PIC S9(9) COMP. 
         03 BNDDFMT           PIC S9(9) COMP. 
         03 BNDDVLN           PIC S9(9) COMP. 
         03 BNDDFMTL          PIC S9(4) COMP. 
         03 BNDDVTYP          PIC S9(4) COMP. 
         03 BNDDI             PIC S9(9) COMP. 
         03 BNDDH-VNAME       PIC S9(9) COMP. 
         03 BNDDH-MAX-VNAMEL  PIC S9(4) COMP. 
         03 BNDDH-CUR-VNAMEL  PIC S9(4) COMP. 
         03 BNDDI-VNAME       PIC S9(9) COMP. 
         03 BNDDI-MAX-VNAMEL  PIC S9(4) COMP. 
         03 BNDDI-CUR-VNAMEL  PIC S9(4) COMP. 
         03 BNDDFCLP          PIC S9(9) COMP. 
         03 BNDDFCRCP         PIC S9(9) COMP. 
 01  XBNDDI. 
     03  BND-DI               OCCURS 2 TIMES PIC S9(4) COMP. 
 01  XBNDDIVNAME. 
     03  BND-DI-VNAME         OCCURS 2 TIMES PIC X(80). 
 01  XBNDDV. 
*    Since you know what the SQL statement will be, you can set 
*    up a two-dimensional table with a maximum of 2 columns and 
*    8 rows.  Each element can be up to 10 characters long.  (You 
*    can alter these values according to your needs.) 
     03  BND-COLUMN           OCCURS 2 TIMES. 
         05  BND-ELEMENT      OCCURS 8 TIMES PIC X(10). 
 01  XBNDDHVNAME. 
     03  BND-DH-VNAME         OCCURS 2 TIMES PIC X(80).  
 01  COLUMN-INDEX             PIC 999. 
 01  ROW-INDEX                PIC 999. 
 01  DUMMY-INTEGER            PIC 9999.
     EXEC SQL BEGIN DECLARE SECTION END-EXEC. 
         01  USERNAME         PIC X(20). 
         01  PASSWD           PIC X(20). 
         01  DYN-STATEMENT    PIC X(80). 
         01  NUMBER-OF-ROWS   PIC S9(4) COMP. 
     EXEC SQL END DECLARE SECTION END-EXEC. 

     EXEC SQL INCLUDE SQLCA END-EXEC. 

 PROCEDURE DIVISION. 
 START-MAIN. 

     EXEC SQL WHENEVER SQLERROR GOTO SQL-ERROR END-EXEC. 

     MOVE "SCOTT" TO USERNAME. 
     MOVE "TIGER" TO PASSWD. 
     EXEC SQL 
         CONNECT :USERNAME IDENTIFIED BY :PASSWD 
     END-EXEC. 
     DISPLAY "Connected to Oracle". 

*    Initialize bind and select descriptors. 
     PERFORM INIT-BNDDSC THRU INIT-BNDDSC-EXIT 
         VARYING COLUMN-INDEX FROM 1 BY 1 
         UNTIL COLUMN-INDEX > 2. 

*    Set up the SQL statement. 
     MOVE SPACES TO DYN-STATEMENT. 
     MOVE "INSERT INTO EMP(EMPNO, DEPTNO) VALUES(:EMPNO,:DEPTNO)" 
         TO DYN-STATEMENT.
     DISPLAY DYN-STATEMENT.

*    Prepare the SQL statement. 
     EXEC SQL 
         PREPARE S1 FROM :DYN-STATEMENT 
     END-EXEC. 

*    Describe the bind variables. 
     EXEC SQL 
         DESCRIBE BIND VARIABLES FOR S1 INTO BNDDSC 
     END-EXEC. 

     PERFORM Z-BIND-TYPE THRU Z-BIND-TYPE-EXIT 
         VARYING COLUMN-INDEX FROM 1 BY 1 
         UNTIL COLUMN-INDEX > 2. 

     IF SQLDFND IN BNDDSC < 0 
         DISPLAY "TOO MANY BIND VARIABLES." 
         GO TO SQL-ERROR 
     ELSE 
         DISPLAY "BIND VARS = " WITH NO ADVANCING 
         MOVE SQLDFND IN BNDDSC TO DUMMY-INTEGER 
         DISPLAY DUMMY-INTEGER 
         MOVE SQLDFND IN BNDDSC TO SQLDNUM IN BNDDSC. 

         MOVE 8 TO NUMBER-OF-ROWS. 
         PERFORM GET-ALL-VALUES THRU GET-ALL-VALUES-EXIT 
             VARYING ROW-INDEX FROM 1 BY 1 
             UNTIL ROW-INDEX > NUMBER-OF-ROWS. 

*    Execute the SQL statement. 
     EXEC SQL FOR :NUMBER-OF-ROWS 
         EXECUTE S1 USING DESCRIPTOR BNDDSC 
     END-EXEC. 

     DISPLAY "INSERTED " WITH NO ADVANCING. 
     MOVE SQLERRD(3) TO DUMMY-INTEGER. 
     DISPLAY DUMMY-INTEGER WITH NO ADVANCING. 
     DISPLAY " ROWS.". 
     GO TO END-SQL. 

 SQL-ERROR. 
*    Display any SQL error message and code. 
     DISPLAY SQLERRMC. 
     EXEC SQL ROLLBACK WORK RELEASE END-EXEC. 
     STOP RUN. 

 END-SQL. 
     EXEC SQL WHENEVER SQLERROR CONTINUE END-EXEC. 
     EXEC SQL COMMIT WORK RELEASE END-EXEC. 
     STOP RUN. 

 INIT-BNDDSC. 
*    Start of COBOL PERFORM procedures, initialize the bind 
*    descriptor. 
     MOVE 80 TO BNDDH-MAX-VNAMEL(COLUMN-INDEX). 
     CALL "SQLADR" USING 
         BND-DH-VNAME(COLUMN-INDEX) 
         BNDDH-VNAME(COLUMN-INDEX). 
     MOVE 80 TO BNDDI-MAX-VNAMEL(COLUMN-INDEX). 
     CALL "SQLADR" USING 
         BND-DI-VNAME(COLUMN-INDEX) 
         BNDDI-VNAME (COLUMN-INDEX). 
     MOVE 10 TO BNDDVLN(COLUMN-INDEX). 
     CALL "SQLADR" USING 
         BND-ELEMENT(COLUMN-INDEX,1) 
         BNDDV(COLUMN-INDEX). 
     MOVE ZERO TO BNDDI(COLUMN-INDEX). 
     CALL "SQLADR" USING 
         BND-DI(COLUMN-INDEX) 
         BNDDI(COLUMN-INDEX). 
     MOVE ZERO TO BNDDFMT(COLUMN-INDEX). 
     MOVE ZERO TO BNDDFMTL(COLUMN-INDEX). 
     MOVE ZERO TO BNDDFCLP(COLUMN-INDEX). 
     MOVE ZERO TO BNDDFCRCP(COLUMN-INDEX). 
 INIT-BNDDSC-EXIT. 
     EXIT.

 Z-BIND-TYPE. 
*    Replace the 0s DESCRIBEd into the datatype table with 1s to 
*    avoid an "invalid datatype" Oracle error. 
     MOVE 1 TO BNDDVTYP(COLUMN-INDEX). 

 Z-BIND-TYPE-EXIT. 
     EXIT. 

 GET-ALL-VALUES. 
*    Get the bind variables for each row. 
     DISPLAY "ENTER VALUES FOR ROW NUMBER ",ROW-INDEX. 
     PERFORM GET-BIND-VARS 
         VARYING COLUMN-INDEX FROM 1 BY 1 
         UNTIL COLUMN-INDEX > SQLDFND IN BNDDSC. 
 GET-ALL-VALUES-EXIT. 
     EXIT. 

 GET-BIND-VARS. 
*    Get the value of each bind variable. 
     DISPLAY "    ENTER VALUE FOR ",BND-DH-VNAME(COLUMN-INDEX) 
         WITH NO ADVANCING. 
     ACCEPT BND-ELEMENT(COLUMN-INDEX,ROW-INDEX). 
 GET-BIND-VARS-EXIT. 
     EXIT.  

サンプル・プログラム10: 動的SQL方法4

このプログラムでは、動的SQL方法4の使用に必要な基本手順を示します。ログインすると、プログラムはSQL文のユーザーの入力を求め、文の準備を行い、カーソルを宣言し、DESCRIBE BINDを使用してバインド変数のチェックを行い、選択リスト変数を記述します。入力されたSQL文が問合せのときは、プログラムは各行のデータをフェッチしてからカーソルをクローズします。

      ***************************************************************
      * Sample Program 10: Dynamic SQL Method 4                     *
      *                                                             *
      * This program shows the basic steps required to use dynamic  *
      * SQL Method 4.  After logging on to ORACLE, the program      *
      * prompts the user for a SQL statement, PREPAREs the          *
      * statement, DECLAREs a cursor, checks for any bind variables *
      * using DESCRIBE BIND, OPENs the cursor, and DESCRIBEs any    *
      * select-list variables.  If the input SQL statement is a     *
      * query, the program FETCHes each row of data, then CLOSEs    *
      * the cursor.                                                 *
      ***************************************************************

       IDENTIFICATION DIVISION.
       PROGRAM-ID.  DYNSQL4.
       ENVIRONMENT DIVISION.
       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  BNDDSC.

           02  SQLDNUM               PIC S9(9) COMP VALUE 20.
           02  SQLDFND               PIC S9(9) COMP.
           02  BNDDVAR               OCCURS 20 TIMES.
               03  BNDDV             PIC S9(9) COMP.
               03  BNDDFMT           PIC S9(9) COMP.
               03  BNDDVLN           PIC S9(9) COMP.
               03  BNDDFMTL          PIC S9(4) COMP.
               03  BNDDVTYP          PIC S9(4) COMP.
               03  BNDDI             PIC S9(9) COMP.
               03  BNDDH-VNAME       PIC S9(9) COMP.
               03  BNDDH-MAX-VNAMEL  PIC S9(4) COMP.
               03  BNDDH-CUR-VNAMEL  PIC S9(4) COMP.
               03  BNDDI-VNAME       PIC S9(9) COMP.
               03  BNDDI-MAX-VNAMEL  PIC S9(4) COMP.
               03  BNDDI-CUR-VNAMEL  PIC S9(4) COMP.
               03  BNDDFCLP          PIC S9(9) COMP.
               03  BNDDFCRCP         PIC S9(9) COMP.

       01  XBNDDI.

           03  BND-DI                OCCURS 20 TIMES PIC S9(4) COMP.

       01  XBNDDIVNAME.
           03  BND-DI-VNAME          OCCURS 20 TIMES PIC X(80).
       01  XBNDDV.
           03  BND-DV                OCCURS 20 TIMES PIC X(80).
       01  XBNDDHVNAME.
           03  BND-DH-VNAME          OCCURS 20 TIMES PIC X(80).

       01  SELDSC.

           02  SQLDNUM               PIC S9(9) COMP VALUE 20.
           02  SQLDFND               PIC S9(9) COMP.
           02  SELDVAR               OCCURS 20 TIMES.
               03  SELDV             PIC S9(9) COMP.
               03  SELDFMT           PIC S9(9) COMP.
               03  SELDVLN           PIC S9(9) COMP.
               03  SELDFMTL          PIC S9(4) COMP.
               03  SELDVTYP          PIC S9(4) COMP.
               03  SELDI             PIC S9(9) COMP.
               03  SELDH-VNAME       PIC S9(9) COMP.
               03  SELDH-MAX-VNAMEL  PIC S9(4) COMP.
               03  SELDH-CUR-VNAMEL  PIC S9(4) COMP.
               03  SELDI-VNAME       PIC S9(9) COMP.
               03  SELDI-MAX-VNAMEL  PIC S9(4) COMP.
               03  SELDI-CUR-VNAMEL  PIC S9(4) COMP.
               03  SELDFCLP          PIC S9(9) COMP.
               03  SELDFCRCP         PIC S9(9) COMP.

       01  XSELDI.

           03  SEL-DI                OCCURS 20 TIMES PIC S9(4) COMP.

       01  XSELDIVNAME.
           03  SEL-DI-VNAME          OCCURS 20 TIMES PIC X(80).
       01  XSELDV.
           03  SEL-DV                OCCURS 20 TIMES PIC X(80).
       01  XSELDHVNAME.
           03  SEL-DH-VNAME          OCCURS 20 TIMES PIC X(80).

       01  TABLE-INDEX               PIC 9(3).
       01  VAR-COUNT                 PIC 9(2).
       01  ROW-COUNT                 PIC 9(4).
       01  NO-MORE-DATA              PIC X(1) VALUE "N".
       01  NULLS-ALLOWED             PIC S9(9) COMP.

       01  PRECISION                 PIC S9(9) COMP.
       01  SCALE                     PIC S9(9) COMP.

       01  DISPLAY-LENGTH            PIC S9(9) COMP.
       01  MAX-LENGTH                PIC S9(9) COMP VALUE 80.
       01  COLUMN-NAME               PIC X(30).
       01  NULL-VAL                  PIC X(80) VALUE SPACES.
           EXEC SQL BEGIN DECLARE SECTION END-EXEC.
       01  USERNAME       PIC X(20).
       01  PASSWD         PIC X(20).
       01  DYN-STATEMENT  PIC X(80).
           EXEC SQL END DECLARE SECTION   END-EXEC.
           EXEC SQL INCLUDE SQLCA         END-EXEC.

       PROCEDURE DIVISION.
       START-MAIN.

           EXEC SQL WHENEVER SQLERROR GOTO SQL-ERROR END-EXEC.

           DISPLAY "USERNAME: " WITH NO ADVANCING.

           ACCEPT USERNAME.

           DISPLAY "PASSWORD: " WITH NO ADVANCING.

           ACCEPT PASSWD.

           EXEC SQL CONNECT :USERNAME IDENTIFIED BY :PASSWD END-EXEC.
           DISPLAY "CONNECTED TO ORACLE AS USER: ", USERNAME.

      *    INITIALIZE THE BIND AND SELECT DESCRIPTORS.

           PERFORM INIT-BNDDSC
               VARYING TABLE-INDEX FROM 1 BY 1 
               UNTIL TABLE-INDEX > 20.

           PERFORM INIT-SELDSC
               VARYING TABLE-INDEX FROM 1 BY 1 
               UNTIL TABLE-INDEX > 20.

      *    GET A SQL STATEMENT FROM THE OPERATOR.

           DISPLAY "ENTER SQL STATEMENT WITHOUT TERMINATOR:".
           DISPLAY ">" WITH NO ADVANCING.

           ACCEPT DYN-STATEMENT.

           DISPLAY " ".

      *    PREPARE THE SQL STATEMENT AND DECLARE A CURSOR.

           EXEC SQL  PREPARE S1 FROM :DYN-STATEMENT  END-EXEC.
           EXEC SQL  DECLARE C1 CURSOR FOR S1        END-EXEC.

      *    DESCRIBE ANY BIND VARIABLES.

           EXEC SQL  DESCRIBE BIND VARIABLES FOR S1 INTO BNDDSC
           END-EXEC.

           IF SQLDFND IN BNDDSC < 0
               DISPLAY "TOO MANY BIND VARIABLES."
               GO TO END-SQL
           ELSE
               DISPLAY "NUMBER OF BIND VARIABLES: " WITH NO ADVANCING
               MOVE SQLDFND IN BNDDSC TO VAR-COUNT
               DISPLAY VAR-COUNT
               MOVE SQLDFND IN BNDDSC TO SQLDNUM IN BNDDSC
           END-IF.

      *    REPLACE THE 0S DESCRIBED INTO THE DATATYPE FIELDS OF THE
      *    BIND DESCRIPTOR WITH 1S TO AVOID AN "INVALID DATATYPE"
      *    ORACLE ERROR

           MOVE 1 TO TABLE-INDEX.
       FIX-BIND-TYPE.
               MOVE 1 TO BNDDVTYP(TABLE-INDEX)
               ADD 1 TO TABLE-INDEX
               IF TABLE-INDEX <= 20
                   GO TO FIX-BIND-TYPE.

      *    LET THE USER FILL IN THE BIND VARIABLES.

           IF SQLDFND IN BNDDSC = 0
               GO TO DESCRIBE-ITEMS.
           MOVE 1 TO TABLE-INDEX.
       GET-BIND-VAR.
               DISPLAY "ENTER VALUE FOR ", BND-DH-VNAME(TABLE-INDEX).

               ACCEPT BND-DV(TABLE-INDEX).

               ADD 1 TO TABLE-INDEX
               IF TABLE-INDEX <= SQLDFND IN BNDDSC
                   GO TO GET-BIND-VAR.

      *    OPEN THE CURSOR AND DESCRIBE THE SELECT-LIST ITEMS.

       DESCRIBE-ITEMS.

           EXEC SQL  OPEN C1 USING DESCRIPTOR BNDDSC          END-EXEC.
           EXEC SQL  DESCRIBE SELECT LIST FOR S1 INTO SELDSC  END-EXEC.

           IF SQLDFND IN SELDSC < 0
               DISPLAY "TOO MANY SELECT-LIST ITEMS."
               GO TO END-SQL
           ELSE
               DISPLAY "NUMBER OF SELECT-LIST ITEMS: "
                   WITH NO ADVANCING
               MOVE SQLDFND IN SELDSC TO VAR-COUNT
               DISPLAY VAR-COUNT
               DISPLAY " "
               MOVE SQLDFND IN SELDSC TO SQLDNUM IN SELDSC
           END-IF.

      *    COERCE THE DATATYPE OF ALL SELECT-LIST ITEMS TO VARCHAR2.

           IF SQLDNUM IN SELDSC > 0
               PERFORM COERCE-COLUMN-TYPE
                   VARYING TABLE-INDEX FROM 1 BY 1
                   UNTIL TABLE-INDEX > SQLDNUM IN SELDSC
               DISPLAY " ".

      *    FETCH EACH ROW AND PRINT EACH SELECT-LIST VALUE.

           IF SQLDNUM IN SELDSC > 0
               PERFORM FETCH-ROWS UNTIL NO-MORE-DATA = "Y".

           DISPLAY " "
           DISPLAY "NUMBER OF ROWS PROCESSED: " WITH NO ADVANCING.
           MOVE SQLERRD(3) TO ROW-COUNT.
           DISPLAY ROW-COUNT.

      *    CLEAN UP AND TERMINATE.

           EXEC SQL  CLOSE C1             END-EXEC.
           EXEC SQL  COMMIT WORK RELEASE  END-EXEC.
           DISPLAY " ".
           DISPLAY "HAVE A GOOD DAY!".
           DISPLAY " ".
           STOP RUN.

      *    DISPLAY ORACLE ERROR MESSAGE AND CODE.

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

      *    PERFORMED SUBROUTINES BEGIN HERE:

      *    INIT-BNDDSC: INITIALIZE THE BIND DESCRIPTOR.

       INIT-BNDDSC.

           MOVE SPACES TO BND-DH-VNAME(TABLE-INDEX).
           MOVE 80 TO BNDDH-MAX-VNAMEL(TABLE-INDEX).
           CALL "SQLADR" USING 
               BND-DH-VNAME(TABLE-INDEX) 
               BNDDH-VNAME(TABLE-INDEX). 
        
           MOVE SPACES TO BND-DI-VNAME(TABLE-INDEX).
           MOVE 80 TO BNDDI-MAX-VNAMEL(TABLE-INDEX).
           CALL "SQLADR" USING 
               BND-DI-VNAME(TABLE-INDEX) 
               BNDDI-VNAME (TABLE-INDEX).
        
           MOVE SPACES TO BND-DV(TABLE-INDEX).
           MOVE 80 TO BNDDVLN(TABLE-INDEX).
           CALL "SQLADR" USING 
               BND-DV(TABLE-INDEX) 
               BNDDV(TABLE-INDEX).
           MOVE ZERO TO BND-DI(TABLE-INDEX).
           CALL "SQLADR" USING 
               BND-DI(TABLE-INDEX) 
               BNDDI(TABLE-INDEX).

           MOVE ZERO TO BNDDFMT(TABLE-INDEX).
           MOVE ZERO TO BNDDFMTL(TABLE-INDEX).
           MOVE ZERO TO BNDDFCLP(TABLE-INDEX).
           MOVE ZERO TO BNDDFCRCP(TABLE-INDEX).

      *    INIT-SELDSC: INITIALIZE THE SELECT DESCRIPTOR.

       INIT-SELDSC.

           MOVE SPACES TO SEL-DH-VNAME(TABLE-INDEX).
           MOVE 80 TO SELDH-MAX-VNAMEL(TABLE-INDEX).
           CALL "SQLADR" USING 
               SEL-DH-VNAME(TABLE-INDEX) 
               SELDH-VNAME(TABLE-INDEX). 
        
           MOVE SPACES TO SEL-DI-VNAME(TABLE-INDEX).
           MOVE 80 TO SELDI-MAX-VNAMEL(TABLE-INDEX).
           CALL "SQLADR" USING 
               SEL-DI-VNAME(TABLE-INDEX) 
               SELDI-VNAME (TABLE-INDEX).

           MOVE SPACES TO SEL-DV(TABLE-INDEX).
           MOVE 80 TO SELDVLN(TABLE-INDEX).
           CALL "SQLADR" USING 
               SEL-DV(TABLE-INDEX) 
               SELDV(TABLE-INDEX).

           MOVE ZERO TO SEL-DI(TABLE-INDEX).
           CALL "SQLADR" USING 
               SEL-DI(TABLE-INDEX) 
               SELDI(TABLE-INDEX).

           MOVE ZERO TO SELDFMT(TABLE-INDEX).
           MOVE ZERO TO SELDFMTL(TABLE-INDEX).
           MOVE ZERO TO SELDFCLP(TABLE-INDEX).
           MOVE ZERO TO SELDFCRCP(TABLE-INDEX).

      *    COERCE SELECT-LIST DATATYPES TO VARCHAR2.

       COERCE-COLUMN-TYPE.
           CALL "SQLNUL" USING
               SELDVTYP(TABLE-INDEX)
               SELDVTYP(TABLE-INDEX)
               NULLS-ALLOWED.

      *    IF DATATYPE IS DATE, LENGTHEN TO 9 CHARACTERS.
           IF SELDVTYP(TABLE-INDEX) = 12
               MOVE 9 TO SELDVLN(TABLE-INDEX).

      *    IF DATATYPE IS NUMBER, SET LENGTH TO PRECISION.
           IF SELDVTYP(TABLE-INDEX) = 2
               CALL "SQLPRC" USING
                   SELDVLN(TABLE-INDEX)
                   PRECISION
                   SCALE.
           MOVE 0 TO DISPLAY-LENGTH.
           IF SELDVTYP(TABLE-INDEX) = 2 AND PRECISION = 0
               MOVE 40 TO DISPLAY-LENGTH.
           IF SELDVTYP(TABLE-INDEX) = 2 AND PRECISION > 0
               ADD 2 TO PRECISION
               MOVE PRECISION TO DISPLAY-LENGTH.

           IF SELDVTYP(TABLE-INDEX) = 2
               IF DISPLAY-LENGTH > MAX-LENGTH
                   DISPLAY "COLUMN VALUE TOO LARGE FOR DATA BUFFER."
                   GO TO END-SQL
               ELSE
                   MOVE DISPLAY-LENGTH TO SELDVLN(TABLE-INDEX).

      *    COERCE DATATYPES TO VARCHAR2.
           MOVE 1 TO SELDVTYP(TABLE-INDEX).

      *    DISPLAY COLUMN HEADING.
           MOVE SEL-DH-VNAME(TABLE-INDEX) TO COLUMN-NAME.
           DISPLAY COLUMN-NAME(1:SELDVLN(TABLE-INDEX)), " "
               WITH NO ADVANCING.

      *FETCH A ROW AND PRINT THE SELECT-LIST VALUE.

       FETCH-ROWS.
           EXEC SQL  FETCH C1 USING DESCRIPTOR SELDSC  END-EXEC.
           IF SQLCODE NOT = 0
               MOVE "Y" TO NO-MORE-DATA.
           IF SQLCODE = 0
               PERFORM PRINT-COLUMN-VALUES
                   VARYING TABLE-INDEX FROM 1 BY 1
                   UNTIL TABLE-INDEX > SQLDNUM IN SELDSC
               DISPLAY " ".

      *PRINT A SELECT-LIST VALUE.

       PRINT-COLUMN-VALUES.
           IF SEL-DI(TABLE-INDEX) = -1
             DISPLAY NULL-VAL(1:SELDVLN(TABLE-INDEX)), " "
                 WITH NO ADVANCING
           ELSE
             DISPLAY SEL-DV(TABLE-INDEX)(1:SELDVLN(TABLE-INDEX)), " "
                 WITH NO ADVANCING
           END-IF.