2.6.6 ストアドプロシジャの利用

ストアドプロシジャを利用するには,データベースの接続時に生成したDBConnectionオブジェクトのCreateCallableStatementメソッドを呼び出し,DBCallableStatementオブジェクトを生成します。次に,SetProcedureメソッドで実行するストアドプロシジャ名を指定し,引数をSetParamメソッドで設定し,Executeメソッドで実行します。

戻り値のあるストアドプロシジャの実行結果は, GetParamメソッドの引数で受け取る方法と,ResultSetで受け取る方法があります。また,これらを併用することもできます。

ストアドプロシジャは,サーバ系の下記DBMSで利用可能でであり,ストアドプロシジャを利用するには,DBCallableStatementクラスを使います。

<この項の構成>
(1) ストアドプロシジャの実行
(2) ストアドプロシジャの戻り値の受け取り(引数)
(3) ストアドプロシジャの戻り値の受け取り(ResultSet)

(1) ストアドプロシジャの実行

ストアドプロシジャを利用するには,データベースの接続時に生成したDBConnectionオブジェクトのCreateCallableStatementメソッドを呼び出し,DBCallableStatementオブジェクトを生成します。次に,SetProcedureメソッドで実行するストアドプロシジャ名を指定し,引数をSetParamメソッドで設定し,Executeメソッドで実行します。

DBConnection*         pConnect;
 DBCallableStatement*  pCall;
 {
   // DBCallableStatementオブジェクトの生成
   pCall = pConnect->CreateCallableStatement("sample1");
   pCall->SetProcedure("Proc100");               // プロシジャ名設定
   pCall->Execute();                             // プロシジャ実行
 }

(a) 引数を持つストアドプロシジャの実行

ストアドプロシジャが引数を持つ場合,SetProcedureメソッドでは,プロシジャ名に続けて"?"を引数分指定します。?パラメタに設定する値は,DBCallableStatementオブジェクトのSetParamメソッドで指定します。SetParamメソッドでは,?パラメタの位置と設定するデータを指定します。

SetParamメソッドで指定する?パラメタの位置は,IN,INOUTパラメタの位置です。OUTは含みません。

 DBConnection*         pConnect;
 DBCallableStatement* pCall;
{
   // DBCallableStatementオブジェクト生成
   pCall = pConnect->CreateCallableStatement("sample1");
   pCall->SetProcedure("Proc_100(?,?)");
                                       // ストアドプロシジャ名設定
   pCall->SetParam(1, pszCity) ;      // パラメタ値の設定
   pCall->Execute();                   // ストアドプロシジャ実行
}

(b) 同期・非同期処理

ストアドプロシジャも通常のデータベースアクセスと同様,DBConnectionオブジェクトの設定により同期・非同期処理が行われます。

 DBConnection*         pConnect;
 DBCallableStatement* pCall;
 {
   // DBCallableStatementオブジェクトの生成
   pCall = pConnect->CreateCallableStatement("sample1");
   pCall->SetProcedure("Proc_100");         // プロシジャ名設定
   pCall->Execute();                        // プロシジャ実行
   pCall->WaitForDataSource(DBR_INFINITE);
                                      // プロシジャ実行完了待ち
}

また,ストアドプロシジャの実行完了確認には,DBCallableStatementオブジェクトのIsCompletedメソッドも使えます。

   if (!pCall->IsCompleted())             // プロシジャ実行の完了確認
   pResultSet = pCall ->GetResultSet();   // 検索結果の受け取り

(2) ストアドプロシジャの戻り値の受け取り(引数)

この項では,戻り値のあるストアドプロシジャの実行結果を,引数で受け取る方法について説明します。

実行結果は DBCallableStatementオブジェクトのGetParamメソッドの引数で受け取ります。データはGetparamメソッドの引数で指定したデータ型で取得します。

void class1::func1(char* pszCity,INT32 dwCount)
{
 DBConnection*           pConnect;
 DBCallableStatement*   pCall;

           // DBCallableStatementオブジェクトの生成
   pCall = pConnect->CreateCallableStatement("sample1");
   pCall->SetProcedure("Proc_100(?,?)");
                                     // ストアドプロシジャ名設定
   pCall->SetParam(1, pszCity) ;    // パラメタ値の設定
   pCall->Execute();                 // ストアドプロシジャ実行
   pCall->WaitForDataSource(DBR_INFINITE);
                                     // プロシジャ実行完了待ち
   pCall->GetParam(1, &dwCount);    // 戻り値の受け取り
}

なお,INパラメタとOUTパラメタの位置はどちらも1になります。

(3) ストアドプロシジャの戻り値の受け取り(ResultSet)

下記DBMSの場合だけ,ストアドプロシジャでのSELECT文の実行結果が複数のレコードでもアプリケーションで受け取ることができます。

この場合,ストアドプロシジャではRESULT句で戻り値の型を定義し,アプリケーションでは,DBResultSetクラスのResultSetとして受取り,GetFieldメソッドで参照します。

(a) ストアドプロシジャの形式

ストアドプロシジャでは,SELECT文の結果を呼び出し元に渡すために,RESULT句で戻り値の型を定義します。

CREATE PROCEDURE sample ()
RESULT ("Value" INT,"Shop" CHAR(30))
BEGIN
 SELECT  CAST( sum( sales_order_items.quantity *
               product.unit_price)
               AS INTEGER ) AS value
               shop_name,
 FROM customer
          INNER JOIN sales_order
          INNER JOIN sales_order_items
          INNER JOIN product
 GROUP BY shop_name
 ORDER BY value desc;
END

この例では,value及びshop_nameの検索結果をResultSetとして扱うために,RESULT句でValue及びShopフィールドとそのデータ型を新しく定義しています。

また,SELECT文中に演算処理がある場合は,その結果を考慮してストアドプロシジャの作成時に,結果に合ったデータ型をRESULT句で用意しておきます。

なお,RESULT句に指定するデータ型は,元テーブルと同じデータ型,又は自動的に変換できるデータ型だけが指定できます。自動変換できるデータ型については,使用するDBMSのドキュメントを参照してください。

(b) ResultSetの参照

ストアドプロシジャの検索結果をResultSetで受け取る場合,DBCallableStatementオブジェクトからGetResultSetメソッドを呼び出して,DBResultSetオブジェクトを生成し,検索結果をResultSetに得ます。ResultSetに読込まれたレコードを参照する方法については,「2.5.2検索レコードの参照」と同様です。

データの型
ストアドプロシジャ内のRESULT句で定義したデータ型とGetFieldメソッドの引数に指定したデータ型とが異なる場合は,引数のデータ型に合わせて変換されます。データ変換については,「7.1 クラスライブラリで扱うデータ型と変換規則」を,変換時の注意事項については「2.5.2 検索レコードの参照」を参照してください。
データ変換
ストアドプロシジャ内のSELECT文の場合も単純なSELECT文の場合も,検索結果をDBResultSetクラスのGetFieldメソッドで参照します。異なる点は,単純なSELECT文の場合データベースでのフィールドの型をGetFieldメソッドの引数に指定したデータ型に変換して参照しますが,ストアドプロシジャの場合RESULT句で定義したフィールドの型をGetFieldメソッドの引数に指定したデータ型に変換して参照します。
その他
ストアドプロシジャを利用した検索でのレコードのロック方法は,SetResultSetTypeメソッドの引数で設定します。
また,検索結果が複数レコードの場合,n件ずつ分割して取得することもできます。このとき一度に取得するレコードの最大数は,SetMaxRowsメソッドで指定します。
(c) 複数ResultSetの参照

ストアドプロシジャ中に,SELECT文が複数記述されているストアドプロシジャでは,SELECT文ごとにResultSetを扱います。

次に,複数のSELECT文を記述したストアドプロシジャの例を示します。

CREATE PROCEDURE ListPeople()
RESULT ( lname CHAR(36), fname CHAR(36) )
BEGIN
       SELECT emp_lname, emp_fname FROM employee;
       SELECT lname, fname FROM customer;
       SELECT last_name, first_name FROM contact;
END

SELECT文ごとにResultSetを返すストアドプロシジャも,RESULT句を指定します。ただし,複数のSELECT文があっても,プロシジャ中にRESULT句は一つだけしか指定できません。このため,指定するSELECT文には次のような制限があります。

アプリケーションでは,これらの制限によって,RESULT句だけを意識すればよくなります。つまり,GetFieldメソッドを使ってアプリケーションから参照する時は,RESULT句で定義したフィールドのデータ型だけを意識します。

プロシジャ内の次のSELECT文に対するResultSetを取得する概念図を図2-4に示します。

図2-4 プロシジャ内の次のSELECT文に対するResultSetを取得する概念図

[図データ]

検索結果の有無確認
複数のSELECT文がある場合,ひとつのResultSetの参照が終わっても,次のResultSetの処理が必要となります。このような場合,IsEOFメソッドとIsCompletedメソッドを利用して判断します。
下記例は,ResultSetの有無をIsCompletedメソッドで確認し,ResultSet中のレコードの確認をIsEOFメソッドで確認しています。

   pCall->Execute();                  // ストアドプロシジャ実行
   if (!pCall->IsCompleted())         // プロシジャ実行の完了確認
   {
       pResultSet = pCall ->GetResultSet(); // 検索結果の受け取り
       while(!pResultSet->IsEOF())
       {
           pResultSet->GetField(1, &nField);      // データの取得
           cout << "  Data=" << nField;
           pResultSet->GetField(2, &pField);
           cout << "  Data=" << pField;
           cout << endl;
           pResultSet->Next();          // 次のレコードの読み込み
       }
       pCall->Resume();                 // 処理の継続
   }

GetResultSetメソッドを実行すると,前に作成したDBResultSetオブジェクトは削除されることに注意してください。
カーソルの移動
複数のSELECT文がある場合,ひとつのResultSetに対する処理が終了した後で,Resumeメソッドを使って,カーソルを以降のResultSetの先頭に位置付ける必要があります。