2023年10月17日火曜日

SQLのMATCH_RECOGNIZEを呼び出すサンプル・アプリケーションを作る

SQLでは正規表現によるパターン一致を行うために、MATCH_RECOGNIZE句が定義されています。オラクル・データベースでは、12cからMATCH_RECOGNIZE句が使えるようになりました。

MATCH_RECOGNIZE句の使い方は、以下のドキュメントに記載されています。

データ・ウェアハウス・ガイド Release 19

AskTOMのOffice Hourで、Chris Saxonさんが初心者向けの解説を行っています。

Visualizing SQL Pattern Matching (MATCH_RECOGNIZE) - A Beginner's Guide
https://asktom.oracle.com/pls/apex/asktom.search?oh=16823

MATCH_RECOGNIZE句ではパターン一致の検出に正規表現を使っていることもあり、なかなか理解が難しいです。

MATCH_RECOGNIZE句を理解するために、テスト・データを生成するAPEXアプリケーションを作成してみました。

MATCH_RECOGNIZE句を含んだSQLとして以下を記述します。申請が2回以上却下され、また、却下される度に金額を増加させて再申請し、最終的に申請が通ったケースを見つけています。

作成したアプリケーションは以下のように動作します。


本記事では、MATCH_RECOGNIZE句を使うアプリケーションの作り方を紹介します。MATCH_RECOGNIZE句自体については、前出のドキュメントやAskTOMのセミナーなどを参照してください。

最初に、アプリケーションで使用する表を準備します。

クイックSQLの以下のモデルから、表MREC_REQUESTSMREC_REQUEST_TRANSITIONSMREC_STATUSESMREC_STATUS_CATEGORIESを作成します。

それぞれの表の役割は以下になります。
  • MREC_REQUESTSは、提出された申請を保持します。
  • MREC_REQUEST_TRANSITIONSは、申請に対して行われた更新の履歴を保持します。
  • MREC_STATUSESは、申請が取り得るステータスを保持するマスター表です。
  • MREC_STATUS_CATEGORIESは、申請のステータスの分類分けを保持します。
APEXアプリケーションは、表MREC_REQUESTSに保存されている申請を更新します。更新を行なった後で、更新履歴を表MREC_REQUEST_TRANSITIONSに保存します。MATCH_RECOGNIZEを使ったパターン一致は、表MREC_REQUEST_TRANSITIONSに保存されている時系列のデータを対象にして実施します。

SQLワークショップクイックSQLを開き左ペインにモデルを記述し、SQLの生成SQLスクリプトを保存レビューおよび実行を行います。


SQLスクリプトが開きます。スクリプトの変更は不要なので、そのまま実行をクリックします。確認のダイアログが開くので、即時実行をクリックします。


4つの表と5つの付帯する索引が作成されます。


この画面では、アプリケーションの作成は行いません。

アプリケーション作成ウィザードを起動します。アプリケーションの名前MATCH_RECOGNIZEサンプルとします。ホームの編集を開き、ホーム・ページ削除します。


ページの追加をクリックし、追加ページに含まれている複数のレポートを選択します。


表のプリフィックスであるMREC_で検索し、検索されたMREC_REQUESTSMREC_REQUEST_TRANSITIONSMREC_STATUSESMREC_STATUS_CATEGORIESをチェックします。

ページの追加を実行します。


選択した4つの表をソースとした、フォーム付きの対話モード・レポートのページが追加されます。


ページ名Requests(ソースが表MREC_REQUESTS)の編集を開き、詳細ホームページとして設定チェックが入っていることを確認します。


Requests以外のページは、すべて詳細管理ページとして設定チェックを入れます。


ページのアイコンがそれぞれまたはスパナになっていることが確認します。

アプリケーションの作成を実行します。


アプリケーションが作成されます。

今回の作業では、表MREC_REQUESTSの更新を行なうフォームのページである、ページ番号のページだけを編集します。その他のページはそのまま使用します。


アプリケーションを実行し、表MREC_STATUSESとMREC_STATUS_CATEGORIESにデータを投入します。

ナビゲーション・メニューの管理からStatusesを開きます。


作成をクリックします。


StatusとしてSUBMITTEDを入力します。この時点では他のステータスは登録されていないため、Prev Status(SUBMITTEDに遷移できるステータスを設定します)は空白にします。

作成をクリックします。


続けて作成を行います。

StatusとしてAPPROVEDPrev StatusとしてSUBMITTEDを選択し、作成を実行します。


同様に、以下の組み合わせでステータスを作成します。

Status - REJECTED, Prev Status SUBMITTED
Status - PAID, Prev Status APPROVED
Status - WITHDRAWN, Prev Status REJECTED

最後にステータスSUBMITTEDを開き、Prev StatusとしてREJECTEDを設定します。


表MREC_STATUSESに保存されたデータは、以下のようになります。申請の状態遷移の定義になっています。


表MREC_STATUS_CATEGORIESについても、データを入力します。

管理Status Categoriesを開きます。


作成をクリックします。


StatusとしてAPPROVEDを選択し、CategoryOPENを入力します。作成をクリックします。


作成を繰り返し、以下のデータを入力します。

Status - APPROVED, Category - OPEN
Status - PAID, Category - CLOSED
Status - REJECTED, Category - OPEN
Status - SUBMITTED, Category - OPEN
Status - WITHDRAWN, Category - CLOSED

MREC_STATUS_CATEGORIESに保存されたデータは以下のようになります。

以上でステータスに関するマスター・データの登録は完了しました。

ページ・デザイナでページ番号Requestのページを開きます。

現時点でのステータスを保持するページ・アイテムP2_PREV_STATUS_IDを作成します。

タイプ非表示セッション・ステートストレージとしてリクエストごと(メモリーのみ)を選択します。

この後に記述するコードでP2_PREV_STATUS_IDを参照するため、保存をクリックします。


レンダリング前ヘッダー前にあるプロセス初期化フォームRequestの直下に、新たにプロセスを作成します。

識別名前ステータスの保存タイプとしてコードを実行を選択します。ソースPL/SQLコードとして以下を記述します。

:P2_PREV_STATUS_ID := :P2_STATUS_ID;

現時点でのステータスを、ページ・アイテムP2_PREV_STATUS_IDへ保存しています。


ページ・アイテムP2_STATUS_IDを選択します。

LOVタイプSQL問合せに変更し、SQL問合せとして以下を記述します。現在のステータスに従って、LOVで選択可能なステータスを制限します。
select status d, id r from mrec_statuses
where
    (:P2_ID is null and status = 'SUBMITTED')
    or
    (:P2_ID is not null and prev_status_id = :P2_PREV_STATUS_ID)
追加値の表示NULL値の表示はともにオフにします。


申請のステータスに応じて、ページ・アイテムの入力を制限します。

ページ・アイテムP2_REQUESTED_BY読取り専用は、タイプアイテムはNULLではないを選択し、アイテムP2_IDを指定します。ページ・アイテムP2_IDに値がないのは申請を新規作成するときだけです。一旦申請を作成した後は、申請者は変更不可になります。


ページ・アイテムP2_REASONP2_AMOUNTは、申請を新規作成するときと却下された後に再申請を行なうときに限り、編集できるようにします。

2つのページ・アイテムを選択します。読取り専用タイプとして行が返されないSQL問合せとして以下を記述します。
select 1 from dual where :P2_ID is null
union
select 1 from mrec_statuses
where id = :P2_PREV_STATUS_ID and status = 'REJECTED'

ページ・アイテムP2_APPROVED_BYを選択します。

読取り専用タイプ行が返されないSQL問合せとして以下を記述します。
select 1 from mrec_statuses
where id = :P2_PREV_STATUS_ID and status = 'SUBMITTED'
申請のステータスがSUBMITTEDのときに1が返されます。ステータスSUBMITTEDからAPPROVEDもしくはREJECTEDに状態遷移するため、その際にはAPPROVED_BYを入力できます。


左ペインでプロセス・ビューを開きます。

申請の更新履歴を保存するプロセスを作成します。作成したプロセスはプロセスプロセス・フォームRequestの直下に配置します。

識別名前更新履歴保存とします。タイプコードを実行ソースPL/SQLコードとして以下のINSERT文を記述します。
insert into mrec_request_transitions(
    request_id, reason, amount, approved_by, status_id
) values (
    :P2_ID, :P2_REASON, :P2_AMOUNT, :P2_APPROVED_BY, :P2_STATUS_ID
);
サーバー側の条件タイプとしてリクエストは値に含まれるを選択し、としてCREATE SAVEを指定します。ボタンCREATEまたはSAVEをクリックしたときに実行されます(DELETEでは実行しない)。


以上でテスト・データを入力する部分はできました。

MATCH_RECOGNIZE句を使った問合せを行なうページを作成します。

SELECT文の記述を簡素化するため、ビューMREC_REQUEST_TRANSITIONS_Vを作成します。

以下のコードを実行します。



MATCH_RECOGNIZEを使ったレポートのページを追加します。

ページの作成を実行します。


対話モード・レポートを選択します。


ページ定義名前MREC_RECOGNIZEとします。ページ・モード標準フォーム・ページを含めるオフです。

データ・ソースソース・タイプとしてSQL問合せを選択し、SQL SELECT文を入力に記事の先頭にあるMATCH_RECOGINIZE句を含んだSELECT文を記述します。ナビゲーションはデフォルトから変更せず、ブレッドクラムの使用ナビゲーションの使用ともにオンとします。

ページの作成をクリックします。


ページが作成されると、アプリケーションは完成です。

アプリケーションを実行すると記事の先頭のGIF動画のように動作します。

現在のアプリケーションでは、MATCH_RECOGNIZE句の正規表現を含む条件の変更は、ページ・デザイナより実施する必要があります。対話モード・レポートのソースタイプSQL問合せからSQL問合せを返すファンクション本体に変更することで、ソースとして実行するSELECT文を動的に変えることができるようになります。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-match-recognize.zip

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