ストアード・プロシージャーから結果セットを受け取るプログラムまたは SQL プロシージャーの作成

内容が分かっている一定の数の結果セット、または内容が分からない可変の数の結果セットをストアード・プロシージャーから受け取るプログラムを作成できます。

既知の数の結果セットを返す場合の方が作成は簡単ですが、可変の数の結果セットを扱うコードを作成すれば、ストアード・プロシージャーが変更された場合にプログラムを大幅に変更せずに済みます。

結果セットを受け取るための基本的な手順は、以下のとおりです。

  1. 返される結果セットごとに、ロケーター変数を宣言します。

    何個の結果セットが返されるか分からない場合には、返される可能性のある結果セットの最大数に応じて、十分な数の結果セット・ロケーターを宣言します。

  2. ストアード・プロシージャーを呼び出し、SQL 戻りコードを調べます。

    CALL ステートメントからの SQLCODE が +466 の場合、ストアード・プロシージャーは結果セットを返しています。

  3. ストアード・プロシージャーが何個の結果セットを返すかを判別します。

    ストアード・プロシージャーが何個の結果セットを返すか既に知っている場合、この手順は省略できます。

    SQL ステートメント DESCRIBE PROCEDURE を使用して、結果セットの数を判別します。 DESCRIBE PROCEDURE は、結果セットに関する情報を SQLDA または SQL 記述子に入れます。

    SQL 記述子の場合、DESCRIBE PROCEDURE ステートメントが完了すると、以下の値を取り出すことができます。

    • DB2_RESULT_SETS_COUNT には、ストアード・プロシージャーによって返される結果セットの数が入ります。
    • 結果セットごとに、以下に示す 1 つの記述子域の項目が返されます。
      • DB2_CURSOR_NAME には、ストアード・プロシージャーが結果セットを返すために使用するカーソルの名前が入ります。
      • DB2_RESULT_SET_ROWS には、結果セット内の行数の推定値が入ります。 値の -1 は、結果セット内の行数の推定値を入手できないことを示します。
      • DB2_RESULT_SET_LOCATOR には、結果セットに関連付けられた結果セット・ロケーターの値が入ります。

    SQLDA の場合、ストアード・プロシージャーが返す可能性のある最大個数の結果セットを保持できるように、SQLDA を十分な大きさに設定します。 DESCRIBE PROCEDURE ステートメントが完了すると、SQLDA 内のフィールドには以下の値が入ります。

    • SQLD には、ストアード・プロシージャーによって返される結果セットの数が入ります。
    • それぞれの SQLVAR 項目に、結果セットに関する情報が記述されます。 SQLVAR 項目には、以下のものが含まれます。
      • SQLNAME フィールドには、ストアード・プロシージャーが結果セットを返すために使用するカーソルの名前が入ります。
      • SQLIND フィールドには、結果セット内の行数の推定値が入ります。 値の -1 は、結果セット内の行数の推定値を入手できないことを示します。
      • SQLDATA フィールドには、結果セット・ロケーターの値 (結果セットのアドレス) が入ります。
  4. 結果セット・ロケーターを結果セットにリンクします。

    SQL ステートメント ASSOCIATE LOCATORS を使用して、結果セット・ロケーターを結果セットにリンクできます。 ASSOCIATE LOCATORS ステートメントは、結果セット・ロケーターの変数に値を割り当てます。 返される結果セットの数よりもロケーターを多く指定した場合、余分のロケーターは無視されます。

    前に DESCRIBE PROCEDURE ステートメントを実行した場合、結果セット・ロケーターの値は、 SQL 記述子内の DB2_RESULT_SET_LOCATOR または SQLDA の SQLDATA フィールドから取得できます。 これらのフィールドの値は、結果セット・ロケーターの変数に手動でコピーすることもできますし、 ASSOCIATE LOCATORS ステートメントを実行してコピーすることもできます。

    ASSOCIATE LOCATORS または DESCRIBE PROCEDURE ステートメントで指定するストアード・プロシージャー名は、 結果セットを返す CALL ステートメントで既に使用されたプロシージャー名でなければなりません。

  5. 結果セットから行を取り出すためのカーソルを割り当てます。

    SQL ステートメント ALLOCATE CURSOR を使用して、各結果セットをカーソルにリンクさせます。 結果セットごとに、1 つの ALLOCATE CURSOR ステートメントを実行します。 これらのカーソル名は、ストアード・プロシージャーにおけるカーソル名と異なっていても構いません。

  6. 結果セットの内容を判別します。

    結果セットのフォーマットを既に知っている場合、この手順は省略できます。

    SQL ステートメント DESCRIBE CURSOR を使用して結果セットのフォーマットを判別し、この情報を SQL 記述子または SQLDA に入れます。 結果セットごとに、結果セット内のすべての列の記述を保持するのに十分な大きさの SQLDA が必要です。

    DESCRIBE CURSOR は、前に ALLOCATE CURSOR を実行したカーソルに対してのみ使用できます。

    DESCRIBE CURSOR を実行した後、結果セットのカーソルが WITH HOLD と宣言される場合、SQL 記述子では DB2_CURSOR_HOLD を調べることができます。 SQLDA では、SQLDA 内のフィールド SQLDAID の 8 バイト目の上位ビットが 1 に設定されます。

  7. ALLOCATE CURSOR ステートメントで割り当てたカーソルを使用して、結果セットから行を取り出してホスト変数に入れます。

    DESCRIBE CURSOR ステートメントを実行した場合は、行を取り出す前に以下の手順を実行してください。

    1. ホスト変数および標識変数用にストレージを割り振ります。 DESCRIBE CURSOR ステートメント実行後の SQL 記述子または SQLDA の内容を使用して、ホスト変数ごとに必要なストレージの大きさを判別します。
    2. 各ホスト変数のストレージのアドレスを、SQLDA の該当する SQLDATA フィールドに入れます。
    3. 各標識変数のストレージのアドレスを、SQLDA の該当する SQLIND フィールドに入れます。

    結果セットからの行の取り出しは、表からの行の取り出しと同じです。

次の例では、これらの各ステップを行う C 言語コードを示します。 他の言語のコーディングも類似しています。

次の例では、返される結果セットの数と各結果セットの内容を知っている場合に、結果セットを受け取る方法を示します。

/*************************************************************/
/* Declare result set locators.  For this example,           */
/* assume you know that two result sets will be returned.    */
/* Also, assume that you know the format of each result set. */
/*************************************************************/
  EXEC SQL BEGIN DECLARE SECTION;
   static volatile SQL TYPE IS RESULT_SET_LOCATOR loc1, loc2;
  EXEC SQL END DECLARE SECTION;
  ⋮
/*************************************************************/
/* Call stored procedure P1.                                 */
/* Check for SQLCODE +466, which indicates that result sets  */
/* were returned.                                            */
/*************************************************************/
  EXEC SQL CALL P1(:parm1, :parm2, ...);
  if(SQLCODE==+466)
  {
  /*************************************************************/
  /* Establish a link between each result set and its          */
  /* locator using the ASSOCIATE LOCATORS.                     */
  /*************************************************************/
    EXEC SQL ASSOCIATE LOCATORS (:loc1, :loc2) WITH PROCEDURE P1;
    ⋮
  /*************************************************************/
  /* Associate a cursor with each result set.                  */
  /*************************************************************/
    EXEC SQL ALLOCATE C1 CURSOR FOR RESULT SET :loc1;
    EXEC SQL ALLOCATE C2 CURSOR FOR RESULT SET :loc2;
  /*************************************************************/
  /* Fetch the result set rows into host variables.            */
  /*************************************************************/
    while(SQLCODE==0)
    {
      EXEC SQL FETCH C1 INTO :order_no, :cust_no;
    ⋮
    }
    while(SQLCODE==0)
    {
      EXEC SQL FETCH C2 :order_no, :item_no, :quantity;
    ⋮
    }
  }

次の例では、返される結果セットの数または各結果セットの内容を知らない場合に、結果セットを受け取る方法を示します。

/*************************************************************/
/* Declare result set locators.  For this example,           */
/* assume that no more than three result sets will be        */
/* returned, so declare three locators.  Also, assume        */
/* that you do not know the format of the result sets.       */
/*************************************************************/
  EXEC SQL BEGIN DECLARE SECTION;
   static volatile SQL TYPE IS RESULT_SET_LOCATOR loc1, loc2, loc3;
  EXEC SQL END DECLARE SECTION;
  ⋮
/*************************************************************/
/* Call stored procedure P2.                                 */
/* Check for SQLCODE +466, which indicates that result sets  */
/* were returned.                                            */
/*************************************************************/
  EXEC SQL CALL P2(:parm1, :parm2, ...);
  if(SQLCODE==+466)
  {
  /*************************************************************/
  /* Determine how many result sets P2 returned, using the     */
  /* statement DESCRIBE PROCEDURE.  :proc_da is an SQLDA       */
  /* with enough storage to accommodate up to three SQLVAR     */
  /* entries.                                                  */
  /*************************************************************/
    EXEC SQL DESCRIBE PROCEDURE P2 INTO :proc_da;
    ⋮
  /*************************************************************/
  /* Now that you know how many result sets were returned,     */
  /* establish a link between each result set and its          */
  /* locator using the ASSOCIATE LOCATORS.  For this example,  */
  /* we assume that three result sets are returned.            */
  /*************************************************************/
    EXEC SQL ASSOCIATE LOCATORS (:loc1, :loc2, :loc3) WITH PROCEDURE P2;
    ⋮
  /*************************************************************/
  /* Associate a cursor with each result set.                  */
  /*************************************************************/
    EXEC SQL ALLOCATE C1 CURSOR FOR RESULT SET :loc1;
    EXEC SQL ALLOCATE C2 CURSOR FOR RESULT SET :loc2;
    EXEC SQL ALLOCATE C3 CURSOR FOR RESULT SET :loc3;

  /*************************************************************/
  /* Use the statement DESCRIBE CURSOR to determine the        */
  /* format of each result set.                                */
  /*************************************************************/
    EXEC SQL DESCRIBE CURSOR C1 INTO :res_da1;
    EXEC SQL DESCRIBE CURSOR C2 INTO :res_da2;
    EXEC SQL DESCRIBE CURSOR C3 INTO :res_da3;
    ⋮
  /*************************************************************/
  /* Assign values to the SQLDATA and SQLIND fields of the     */
  /* SQLDAs that you used in the DESCRIBE CURSOR statements.   */
  /* These values are the addresses of the host variables and  */
  /* indicator variables into which DB2 will put result set    */
  /* rows.                                                     */
  /*************************************************************/
    ⋮
  /*************************************************************/
  /* Fetch the result set rows into the storage areas          */
  /* that the SQLDAs point to.                                 */
  /*************************************************************/
    while(SQLCODE==0)
    {
      EXEC SQL FETCH C1 USING :res_da1;
    ⋮
    }
    while(SQLCODE==0)
    {
      EXEC SQL FETCH C2 USING :res_da2;
    ⋮
    }
    while(SQLCODE==0)
    {
      EXEC SQL FETCH C3 USING :res_da3;
    ⋮
    }
  }

次の例では、SQL 記述子を使用して結果セットを受け取る方法を示します。

これは、呼び出される SQL プロシージャーです。

create procedure owntbl()  
  dynamic result sets 1                         
  begin                                         
    declare c1 cursor for                       
      select name, dbname from qsys2.systables  
        where creator = system_user ;           
    open c1 ;                                   
    return ;                                    
  end                                           
これは、結果セットを処理するプログラムです。
#include <string.h> 
#include <stdio.h>
#include <stdlib.h>

EXEC SQL INCLUDE SQLCA;
/*************************************************************/
/* Declare result set locators.  For this example,           */
/* you know that only one result set will be returned,       */
/* so only one locator is declared.                          */
/*************************************************************/
EXEC SQL BEGIN DECLARE SECTION;
 static volatile SQL TYPE IS RESULT_SET_LOCATOR loc1;
 struct {
   short len;
   char  data[128];
 } tblName;   /* table name  */

 struct {
   short len;
   char  data[128];
 } schName;   /* schema name */
EXEC SQL END DECLARE SECTION;

void main(int argc, char* argv[])
{
/*************************************************************/
/* Call the procedure that might return a result set.  Check */
/* the returned SQLCODE to see if result sets were returned. */
/*************************************************************/
  int noMoreData = 0;
  EXEC SQL WHENEVER SQLERROR GOTO error;
  EXEC SQL CALL OWNTBL ;
  if (SQLCODE != 466) {
    goto error;
  }
/*************************************************************/
/* Since you know only one result set can be returned from   */
/* this procedure, associate a locator with the result set   */
/* and define a cursor to be used with it.                   */
/*************************************************************/
  EXEC SQL ASSOCIATE LOCATORS (:loc1) WITH PROCEDUREOWNTBL ;
  EXEC SQL ALLOCATE C1 CURSOR FOR RESULT SET :loc1 ;
/*************************************************************/
/* Define the descriptor to use for fetching data from the   */
/* cursor.                                                   */
/*************************************************************/
  EXEC SQL ALLOCATE DESCRIPTOR 'desc' WITH MAX 10 ;
  EXEC SQL DESCRIBE CURSOR C1 USING SQL DESCRIPTOR 'desc' ;
  EXEC SQL WHENEVER NOT FOUND GOTO enddata;
  while ( noMoreData == 0 ) {
    EXEC SQL FETCH C1 INTO SQL DESCRIPTOR 'desc' ;
    memset(tblName.data,0x00,sizeof(tblName.data));
    memset(schName.data,0x00,sizeof(schName.data));
    EXEC SQL GET DESCRIPTOR 'desc' VALUE 1 :tblName = DATA;
    EXEC SQL GET DESCRIPTOR 'desc' VALUE 2 :schName = DATA;
    printf("Table: %s   Schema: %s ¥n",
           tblName.data,schName.data);
  }

enddata:
  printf("All rows fetched.¥n");
  return;

error:
  printf("Unexpected error, SQLCODE = %d ¥n", SQLCODE);
  return;
}

次の例では、SQL プロシージャーを使用して結果セットを受け取る方法を示します。 これは、大きな SQL プロシージャーの断片に過ぎません。

DECLARE RESULT1 RESULT_SET_LOCATOR VARYING;           
DECLARE RESULT2 RESULT_SET_LOCATOR VARYING;           
⋮
CALL TARGETPROCEDURE();

ASSOCIATE RESULT SET LOCATORS(RESULT1,RESULT2)
  WITH PROCEDURE TARGETPROCEDURE;                     
ALLOCATE RSCUR1 CURSOR FOR RESULT1;                   
ALLOCATE RSCUR2 CURSOR FOR RESULT2;                   

WHILE AT_END = 0 DO                                 
  FETCH RSCUR1 INTO VAR1;                   
  SET TOTAL1 = TOTAL1 + VAR1;                         
END WHILE;                                             

WHILE AT_END = 0 DO                                 
  FETCH RSCUR2 INTO VAR2;                   
  SET TOTAL2 = TOTAL2 + VAR2;                         
END WHILE;                                             
⋮