2022年3月17日木曜日

対話グリッドを使って検索条件を動的に追加する

 対話グリッドに検索条件を設定し、その条件で表の検索を実施します。APEXのサンプル・データセットとして提供されている表EMPを検索対象とし、列SALと列COMMを検索条件として増減させます。

条件を設定する対話グリッドの実装は、以前の記事 - APEXコレクションを対話グリッドで扱う - を、ほとんどそのまま流用します。設定の詳細は元記事を参照していただくとして、実際に行った実装について説明していきます。

サンプルを実装するアプリケーションを作成します。

アプリケーション作成ウィザードを実行し、名前検索条件の生成とし、アプリケーションの作成を実行します。

最初にAPEXコレクションを初期化します。コレクション名はQUERY_CONDITIONSとします。

共有コンポーネントアプリケーション・プロセスを開き、作成を実行します。


アプリケーション・プロセス名前APEXコレクションの初期化とし、ポイントとして新規インスタンス(新規セッション)開始時を選択します。

へ進みます。


コードとして以下を記述します。プロシージャAPEX_COLLECTION.CREATE_OR_TRUNCATE_COLLECTIONを呼び出し、APEXコレクションQUERY_CONDITIONSの作成もしくは初期化を実行します。

apex_collection.create_or_truncate_collection(
p_collection_name => 'QUERY_CONDITIONS'
);

へ進みます。


条件タイプは選択せず(無条件で実行する)、プロセスの作成をクリックします。


以上で新規セッションの開始時に、画面から設定する検索条件を保持するAPEXコレクションQUERY_CONDITIONSが作成、または既に作成済みであれば初期化されるようになりました。


続いてページ・デザイナホーム・ページを開き、対話グリッドのリージョンを作成します。

識別名前検索条件タイプとして対話グリッドを選択します。ソースタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select
seq_id
, c001
, n001
, n002
from apex_collections
where collection_name = 'QUERY_CONDITIONS'


Attributes編集有効ONにします。


APEXコレクションではSEQ_IDが一意キーとなります。それを対話グリッドに反映させるため、SEQ_IDを選択し、識別タイプ非表示にし、ソース主キーONにします。


C001を選択します。この列で、検索条件の対象とする列を選択します。

識別タイプ選択リストに変更します。ヘッダー対象検証必須の値ONにします。


LOV静的値をクリックし、設定ダイアログを開きます。表示値戻り値の組み合わせは、給与 - SAL手当 - COMMです。戻り値は表EMPの列名とします。OKをクリックします。

LOV追加値の表示NULL値の表示は共にOFFにします。


N001を選択し、ヘッダー最小とします。


N002を選択し、ヘッダー最大とします。


左ペインでプロセス・ビューを開き、プロセス検索条件 - 対話グリッド・データの保存を選択します。

設定ターゲット・タイプPL/SQL Codeに変更し、挿入/更新/削除するPL/SQLコードに以下を記述します。APEXコレクションQUERY_CONDITIONSへ行の挿入/更新/削除をおこなっています。

begin
case :APEX$ROW_STATUS
when 'C' then
:SEQ_ID := apex_collection.add_member(
p_collection_name => 'QUERY_CONDITIONS',
p_c001 => :C001,
p_n001 => :N001,
p_n002 => :N002
);
when 'U' then
apex_collection.update_member(
p_collection_name => 'QUERY_CONDITIONS',
p_seq => :SEQ_ID,
p_c001 => :C001,
p_n001 => :N001,
p_n002 => :N002
);
when 'D' then
apex_collection.delete_member(
p_collection_name => 'QUERY_CONDITIONS',
p_seq => :SEQ_ID
);
end case;
end;

失われた更新の防止OFF行のロックNoにします。


この時点で対話グリッドにて、検索条件の指定ができるようになっています。アプリケーションを実行すると動作を確認できます。


対話グリッドで指定した条件を適用して表EMPを検索し、結果を表示するレポートを作成します。

リージョンを作成します。

識別タイトル従業員一覧タイプ対話モード・レポートを選択します。ソースタイプとしてSQL問合せを返すファンクション本体を選択し、SQL問合せを戻すPL/SQLファンクション本体として以下を記述します。

レポートの表示のために実行されるSELECT文を動的に生成しています。

declare
l_cond varchar2(32767);
begin
-- 追加する検索条件は l_cond に保持する。
l_cond := '';
for c in (
select c001, n001, n002 from apex_collections
where collection_name = 'QUERY_CONDITIONS'
)
loop
if c.c001 = 'SAL' then
l_cond := l_cond || ' and sal between ' || c.n001 || ' and ' || c.n002;
elsif c.c001 = 'COMM' then
l_cond := l_cond || ' and comm between ' || c.n001 || ' and ' || c.n002;
end if;
end loop;
-- 実際に実行するSELECT文を返す。
return 'select empno, ename, sal, comm, deptno from emp where 1=1 ' || l_cond;
end;


対話グリッドを保存したときにリージョン従業員一覧がリフレッシュされるよう、動的アクションを作成します。

リージョン検索条件の上でコンテキスト・メニューを表示させ、動的アクションの作成を実行します。


作成した動的アクションの識別名前検索条件の確定とします。タイミングイベントとして保存[対話グリッド]を選択します。選択タイプリージョンリージョンとして検索条件を選択します。


動的アクションとして実行されるTrueアクション(デフォルトで表示となっている)を選択します。

識別アクションとしてリフレッシュ影響を受ける要素選択タイプとしてリージョンリージョンには従業員一覧を選択します。


ここまでで出来上がったアプリケーションは以下のように動作します。


 レポートが実行するSQLが動的に生成されているので、毎回SQLのハードパースが実行される可能性があります。パフォーマンス面が懸念されることもあるため、レポートのSQLを静的なSELECT文にする実装に変えてみます。

利用者およびSQLの実行回数が少なく、相対的にSELECT文の処理時間が長ければ、SQLのパースにかかる時間は気にしなくてもよいかとは思います。与える検索条件は、動的にSQLを生成する方が柔軟に定義できるでしょう。

今回の検索対象となる列はSALとCOMMです。リージョン検索条件に、以下のページ・アイテムタイプ非表示として作成します。

P1_SAL_C
P1_SAL_MIN
P1_SAL_MAX
P1_COMM_C
P1_COMM_MIN
P1_COMM_MAX


リージョン従業員一覧ソースタイプSQL問合せに変更し、SQL問合せとして以下を記述します。

select empno, ename, sal, comm, deptno from emp
where 1=1
and
(
:P1_SAL_C = 'ALL'
or
sal between :P1_SAL_MIN and :P1_SAL_MAX
)
and
(
:P1_COMM_C = 'ALL'
or
comm between :P1_COMM_MIN and :P1_COMM_MAX
)


今回は動的アクションではなく、検索ボタンを押してリージョン従業員一覧を再表示させます。(APEXコレクションからページ・アイテムへの情報のコピーは、プロセスで実装します。そのため、動的アクションでリージョン従業員一覧を再表示するのは難しくなります。)

作成済みの動的アクション検索条件の確定は不要です。削除します。


検索条件の確定とリージョン従業員一覧の再表示は検索ボタンを作って行うため、対話グリッドの保存ボタンも不要です。

対話グリッド検索条件Attributesツールバーコントロールから、保存ボタンチェックを外します。この対話グリッドは検索条件の指定にしか使用しないので、もっと無効にするほうが良いコントロールや機能はあると思いますが、今の所これだけを無効にします。


リージョン検索条件にページを再表示するボタン検索を作成します。

識別ボタン名B_SUBMITラベル検索とします。アクションはデフォルトのページの送信のままにします。


動作自体に影響はありませんが見栄えを良くするため、外観ホットONにし、テンプレート・オプションを開いて、詳細WidthStrechに変更します。


レポートのソースで使用するページ・アイテムに検索条件を設定するため、新たにプロセスを作成します。

識別名前セッション・ステートの設定ソースPL/SQLコードとして以下を記述します。

begin
-- 検索条件の初期化
apex_util.set_session_state('P1_SAL_C','ALL',false);
apex_util.set_session_state('P1_COMM_C','ALL',false);
-- 検索条件のページ・アイテムの設定
for c in (
select c001, n001, n002 from apex_collections
where collection_name = 'QUERY_CONDITIONS'
)
loop
apex_util.set_session_state('P1_' || c.c001 || '_C','', false);
apex_util.set_session_state('P1_' || c.c001 || '_MIN', c.n001, false);
apex_util.set_session_state('P1_' || c.c001 || '_MAX', c.n002, false);
end loop;
end;

サーバー側の条件ボタン押下時B_SUBMITを選択します。


以上でアプリケーションは完成です。

実行すると本記事の先頭にあるGIF動画の動作を確認できます。

動的なSQLと動的アクションを使っているアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/igdynsearch1.sql
静的なSQLと検索ボタンを使っているアプリケーションのエクスポートは以下です。
https://github.com/ujnak/apexapps/blob/master/exports/igdynsearch2.sql

Oracle APEXのアプリケーション作成の参考になれば幸いです。