CLOSE

前回の記事「ダイナミックに SQL を実行!~動的 SQL の使い方【前編】~」では、動的 SQL の基本と、データを取得しないシンプルな「構文 1」、「構文 2」について解説しました。 構文 1 や 2 は、テーブル名を動的に変えて UPDATE したり、埋め込み SQL では書けない DDL 文(CREATE TABLE など)を発行したりするのには非常に便利でしたね。

しかし、実務で一番やりたいことといえば、「検索条件(WHERE 句)を動的に組み立てて、その検索結果(結果集合)を取得すること」ではないでしょうか? 「条件によって SQL を組み立てたいけど、SELECT 文の結果を受け取りたいときはどうするの?」とモヤモヤしていた方、お待たせしました。

今回は、動的 SQL の真骨頂ともいえる、結果集合を返すことができる「構文 3」と「構文 4」についてご紹介します。 これらは、コンパイル時に入力パラメーターや結果の列がわかっている場合(構文 3)、あるいはそれすらもわからない場合(構文 4)に対応できる強力な機能です。

それでは今回も、ダイナミックにいってみましょう!

[構文 3] 入力パラメーターと結果の列がわかっている SQL

構文 3 は、コンパイルの時点で「どんな列が SELECT されるか」と「どんな入力パラメーターが必要か」がわかっている場合に使用します。 記述方法は埋め込み SQL のカーソル処理に近いですが、テーブル名や WHERE 句を動的に変更できるため、汎用性が高まります。

[構文]

DECLARE Cursor | Procedure DYNAMIC CURSOR | PROCEDURE FOR DynamicStagingArea ;
PREPARE DynamicStagingArea FROM SQLStatement {USING TransactionObject} ;
OPEN DYNAMIC Cursor {USING ParameterList} ;
FETCH Cursor | Procedure INTO HostVariableList ;
CLOSE Cursor | Procedure ;

構文 3 では、DECLARE(宣言)、PREPARE(準備)、OPEN(カーソルを開く)、FETCH(取得)、CLOSE(閉じる)という一連の手順を踏みます。 ここでも構文 2 と同様に、SQL の情報を保持するために DynamicStagingArea(通常はデフォルトのグローバル変数 SQLSA)を使用します,。

では、例を見てみましょう。 従業員テーブル(EMPLOYEE)から、指定した「州(State)」に住む従業員の ID を取得する処理です。

integer li_emp_id
string  ls_state = "MA"
string  ls_sql

// 1. カーソルの宣言 (SQLSA を使用)
DECLARE my_cursor DYNAMIC CURSOR FOR SQLSA ;

// 2. SQL 文の準備 (WHERE 句にパラメーター ? を使用)
ls_sql = "SELECT emp_id FROM employee WHERE state = ?"
PREPARE SQLSA FROM :ls_sql ;

// 3. カーソルをオープン (パラメーターの値を渡す)
OPEN DYNAMIC my_cursor USING :ls_state ;

// 4. データのフェッチ (変数に値を取得)
FETCH my_cursor INTO :li_emp_id ;

// ループ処理などは埋め込み SQL と同様に行えます
DO WHILE SQLCA.SQLCode = 0
    // 処理...
    FETCH my_cursor INTO :li_emp_id ;
LOOP

// 5. カーソルをクローズ
CLOSE my_cursor ;

埋め込み SQL との違いは、DECLAREDYNAMIC CURSOR FOR SQLSA と指定している点と、PREPARE 文が登場している点ですね。 FETCH 文の INTO 句で受け取る変数の数や型が、コンパイル時に確定しているなら、この構文 3 が一番扱いやすいです。

[構文 4] 入力も結果の列もわからない!究極の動的 SQL

さて、いよいよ真打ち登場、「構文 4」の解説です。 これは、コンパイル時には「入力パラメーターの数」も「結果セットの列の数や型」もわからない(あるいはどちらかが不確定)場合に使用します。

例えば、「ユーザーが画面に入力した任意の SQL 文を実行させて結果を表示する」ような汎用的なツールを作る場合、どんな SELECT 文が来るか開発時にはわかりませんよね? そんな時に活躍するのが構文 4 です。

[構文]

DECLARE Cursor | Procedure DYNAMIC CURSOR | PROCEDURE FOR DynamicStagingArea ;
PREPARE DynamicStagingArea FROM SQLStatement {USING TransactionObject} ;
DESCRIBE DynamicStagingArea INTO DynamicDescriptionArea ;
OPEN DYNAMIC Cursor USING DESCRIPTOR DynamicDescriptionArea ;
FETCH Cursor | Procedure USING DESCRIPTOR DynamicDescriptionArea ;
CLOSE Cursor | Procedure ;

構文 4 の最大の特徴は、DESCRIBE 文と DynamicDescriptionArea(通常はデフォルトのグローバル変数 SQLDA)を使用することです。

PREPARE した SQL 文を DESCRIBE することで、「どんな列が返ってくるか」「どんな型のパラメーターが必要か」といった情報を SQLDA に格納します。PowerBuilder はこの情報を元に動的にデータをやり取りします。

例を見てみましょう。ここでは、どんな SELECT 文が来ても、最初の列(文字列型と仮定)を取得する例です。

string ls_sql
string ls_result

ls_sql = "SELECT emp_lname FROM employee" // 実行時に決まる想定

// 1. 準備
PREPARE SQLSA FROM :ls_sql ;

// 2. SQL の内容を解析して SQLDA に格納
DESCRIBE SQLSA INTO SQLDA ;

// 3. カーソル宣言
DECLARE my_cursor DYNAMIC CURSOR FOR SQLSA ;

// 4. オープン (SQLDA を使用)
OPEN DYNAMIC my_cursor USING DESCRIPTOR SQLDA ;

// 5. フェッチ (SQLDA にデータが入る)
FETCH my_cursor USING DESCRIPTOR SQLDA ;

// 6. データの取り出し
// SQLDA.OutParmType 配列でデータ型を確認して適切な関数で取得します
CHOOSE CASE SQLDA.OutParmType[1]
    CASE TypeString!
        // 文字列の場合の取得関数
        ls_result = SQLDA.GetDynamicString(1)
    CASE TypeInteger!
        // 数値の場合など、型に合わせて分岐が必要
        // ...
END CHOOSE

CLOSE my_cursor ;

構文 4 では、FETCH しても直接変数に入るわけではなく、いったん SQLDA という記述領域に格納されます。 そのため、GetDynamicStringGetDynamicNumber といった専用の関数を使って、SQLDA から値を取り出す必要があります。 また、SQLDA.NumOutputs(出力項目の数)や SQLDA.OutParmType(項目の型)を参照することで、どんな結果が返ってきても柔軟に対応できる仕組みを作ることができます。

後編のまとめ

今回は、結果集合を取得できる「構文 3」と「構文 4」を紹介しました。

  • 構文 3: SELECT する列が決まっているならこれ。動的な WHERE 条件などに便利。
  • 構文 4: SELECT する列すらわからないならこれ。SQLDA を駆使する上級者向け。

構文 4 は非常に強力ですが、コードが複雑化しやすく、保守の難易度が上がります。「SELECT する列が決まっている」のであれば、まずは構文 3 での実装を検討するのが、保守性の高いアプリケーションを保つためのコツです。
(SyntaxFromSQL で動的にデータウィンドウを作る方法でも代用できますが…)

動的 SQL を使いこなせば、PowerBuilder アプリケーションの柔軟性がグッと上がります。 ぜひ、適材適所で活用してみてください!

以上、エイタでした!

x instagram facebook youtube