Oracle Databaseに作成したプロパティ・グラフはPGQL(プロパティ・グラフ問合せ言語)を使って操作しますが、2層モードで扱う範囲であればPGQLはSQLに変換されて、Oracle Databaseで実行されます。
そこで、SQLclを使ってPGQLから変換されたSQLを取り出し、Oracle APEXのレポートのソースにしてみます。
PGQLやプロパティ・グラフについて勉強をするための準備作業なので、それらについては特に触れません。@ryotayamanakaさんのこちらの記事 などを手掛かりにして、勉強を始めるのも良いでしょう。
この記事では、SQLを取り出す準備作業とレポートの生成のために実施した作業を記述します。作業にはAlways FreeのAutonomous Databaseのインスタンスを使用します。
今回の作業を行う上で参考にした記事は以下です。
Executing Property Graph (PGQL) queries in SQLcl
https://blogs.oracle.com/oraclespatial/post/executing-property-graph-pgql-queries-in-sqlcl
それでは、SQLclでPGQLを実行するための準備から始めます。
Oracle APEXのワークスペースとしてAPEXDEV が作成済みとします。
この作業を行うAutonomous Databaseのデータベース・アクション に接続します。
管理 のデータベース・ユーザー を開きます。
APEXのワークスペース・スキーマに対して、
RESTの有効化 と
グラフ有効化 を実施します。Oracle APEX 22.1以降で作成したワークスペースでは、
WKSP_ という接頭辞がワークスペース・スキーマに付加されています。
グラフ とWebアクセス の両方をON に変更します。また、SQLclで接続する際に使用するパスワード を設定します。REST別名 に接頭辞wksp_ が付加されている場合は、それを削除しておきます。
APEXのワークスペース・スキーマの名称が変更された理由のひとつは、データを保持しているスキーマ名の露出を避けることであるため、REST別名もワークスペース・スキーマ名と異なる名前にします。
変更の適用 をクリックします。
データベース・ユーザーの設定は以上で完了です。
Autonomous Databaseの詳細 画面を開きます。
SQLclでの接続に使用するウォレットをダウンロードします。DB接続 をクリックします。
ウォレット・タイプ としてインスタンス・ウォレット を選択し、ウォレットのダウンロード を実行します。
パスワード を入力し、ダウンロード を実行します。今回のウォレットの用途では、パスワードが必要になることはありません。
ウォレットがWallet_インスタンス名.zip というファイル名で、手元のPCにダウンロードされます。
最新のSQLclをダウンロードするリンクは以下です。
https://download.oracle.com/otn_software/java/sqldeveloper/sqlcl-latest.zip
また、SQLclの実行に必要なJavaは以下よりダウンロードできます。
https://www.oracle.com/java/technologies/downloads/
SQLclのインストールはZIPファイルを展開してパスを通すだけなので、今回の説明からは割愛します。
SQLcl(Java含む)とウォレットの準備ができたので、SQLclのPGQLプラグインをダウンロードし、利用できるようにします。
Oracle Graph Server and Client Downloads のページを開きます。
https://www.oracle.com/database/technologies/spatialandgraph/property-graph-features/graph-server-and-client/graph-server-and-client-downloads.html この中の
PGQL Plugin for SQLcl を開きます。
Oracle Graph PGQL Plugin for SQLcl をダウンロードします。
V1021724-01.zip (バージョンによってファイル名は異なります)がダウンロードされます。このファイルを解凍すると、
oracle-graph-sqlcl-plugin-22.3.0.zip (バージョンによってファイル名は異なります)が取り出されます。
このファイルをSQLclのホーム・ディレクトリの
lib/ext 以下に展開します。
unzip -d <SQLclを展開したディレクトリ>/sqlcl/lib/ext oracle-graph-sqlcl-plugin-22.3.0.zip
以上でSQLclにPGQLプラグインが組み込まれました。
SQLclを起動しAPEXのワークスペース・スキーマに接続します。
sql -cloudconfig Wallet_<インスタンス名>.zip wksp_<ワークスペース名>/<パスワード>@<インスタンス名>_low
以下のDDLを実行し、プロパティ・グラフの元となるデータを保持する表
SQLCL_BANK_ACCTS と
SQLCL_BANK_TXN Sを作成します。
この後にOracle APEXのデータ・ジェネレータ を使用してテスト・データを生成するために、表SQLCL_BANK_TXNSに主キー列TXN_IDを追加した上で、各種制約を追加しています。
create table sqlcl_bank_accts (
acct_id number not null constraint sqlcl_bank_accts_acct_id_pk primary key,
name varchar2(64) not null
)
;
create table sqlcl_bank_txns (
txn_id number not null constraint sqlcl_bank_txns_txn_id_pk primary key,
from_acct_id number not null
constraint sqlcl_bank_txns_from_acct_i_fk
references sqlcl_bank_accts on delete cascade,
to_acct_id number not null
constraint sqlcl_bank_txns_to_acct_id_fk
references sqlcl_bank_accts on delete cascade,
description varchar2(128) not null,
amount number(38,2) not null
)
;
create index sqlcl_bank_txns_i1 on sqlcl_bank_txns (from_acct_id);
create index sqlcl_bank_txns_i52 on sqlcl_bank_txns (to_acct_id);
SQL> create table sqlcl_bank_accts (
2 acct_id number not null constraint sqlcl_bank_accts_acct_id_pk primary key,
3 name varchar2(64) not null
4 )
5* ;
Table SQLCL_BANK_ACCTSは作成されました。
SQL> create table sqlcl_bank_txns (
2 txn_id number not null constraint sqlcl_bank_txns_txn_id_pk primary key,
3 from_acct_id number not null
4 constraint sqlcl_bank_txns_from_acct_i_fk
5 references sqlcl_bank_accts on delete cascade,
6 to_acct_id number not null
7 constraint sqlcl_bank_txns_to_acct_id_fk
8 references sqlcl_bank_accts on delete cascade,
9 description varchar2(128) not null,
10 amount number(38,2) not null
11 )
12* ;
Table SQLCL_BANK_TXNSは作成されました。
SQL> create index sqlcl_bank_txns_i1 on sqlcl_bank_txns (from_acct_id);
Index SQLCL_BANK_TXNS_I1は作成されました。
SQL> create index sqlcl_bank_txns_i52 on sqlcl_bank_txns (to_acct_id);
Index SQLCL_BANK_TXNS_I52は作成されました。
SQL>
SQLワークショップ のユーティリティ より、データ・ジェネレータ を起動します。
ブループリントの作成 をクリックします。
既存の表の使用 を選択します。
リストから選択 を選び、ブループリント名 としてbanktransfers を入力します。
SQLCL_BANK_ACCTS にチェック を入れ、行 を1000 とします。テスト・データを1000行挿入します。また、SQLCL_BANK_TXNS にチェック を入れ、行を10000 とします。テスト・データを10000行挿入します。
ブループリントが作成されます。
参照制約が適切に設定されていると、SQLCL_BANK_ACCTS、SQLCL_BANK_TXNSの順に配置されます。もし逆になっていると、現在のバージョンのデータ・ジェネレータでは順番を変更する方法がありません。 そのような場合は表ごとにブループリントを分けて作成し、最初にSQLCL_BANK_ACCTSにテスト・データを挿入してください。
SQLCL_BANK_ACCTS の列ACCT_ID を選択し、データ・ソース を順序 、開始 を1 、増分 を1 とします。SQLCL_BANK_ACCTSには1000行のデータを挿入する予定なので、ACCT_IDは1から1000の値が設定されます。
列NAME のデータ・ソース は組込み 、組込み としてperson.last_commna_first(姓、名) を選択します。
SQLCL_BANK_TXNS の列TXN_ID もデータ・ソース は順序 、開始 は1 、増分 は1 とします。
列FROM_ACCT_ID のデータ・ソース は式 、式 として以下を入力します。1から1000までの整数を入力します。
ceil(dbms_random.value(1,1000))
列TO_ACCT_ID はFROM_ACCT_IDと同じ設定を行います。
列DESCRIPTION には任意の文字列が生成されます。最大長 が128なのは少し長いので64 に変更します。
列AMOUNT のデータ・ソース は組込み 、組込み としてnumber.random(乱数) を選択します。最小値 は1000、最大値 は10000000、値の精度 として0 を設定します。一千円から一千万円の間の金額になります。
以上で保存 をクリックします。続けてデータの生成 をクリックします。
アクション としてデータベースへの挿入 を選択し、テスト・データを直接データベースに挿入します。
データの挿入 をクリックします。
エラーが表示されていますが、いくつかのテスト・データの挿入で発生したもので全体が失敗したわけではありません。このまま取消 をクリックしてデータ・ジェネレータを終了します。
以上で表SQLCL_BANK_ACCTS、SQLCL_BANK_TXNSにテスト・データが投入されました。
SQLclより投入されたデータを確認します。表SQLCL_BANK_ACCTSには1000行挿入されています。
SQL> select count(*) from sqlcl_bank_accts;
COUNT(*)
___________
1000
SQL>
表SQLCL_BANK_TXNSには9979行挿入されています。いくつかのテスト・データの挿入に失敗しています。
SQL> select count(*) from sqlcl_bank_txns;
COUNT(*)
___________
9979
SQL>
テスト・データより、送金元と送金先が同じ行を削除します。
SQL> delete from sqlcl_bank_txns where from_acct_id = to_acct_id;
8行削除されました。
SQL> commit;
コミットが完了しました。
SQL>
以上でテスト・データの準備も完了しました。
この表を元にプロパティ・グラフSQLCL_BANK_GRAPH を作成します。英語の元記事で実行しているPGQLは以下になります。最初にPGQLのモードに切り替えます。
pgql auto on;
create property graph sqlcl_bank_graph
vertex tables (
SQLCL_BANK_ACCTS
Key (ACCT_ID)
Label ACCOUNT
Properties (ACCT_ID)
)
edge tables (
SQLCL_BANK_TXNS
key(TXN_ID)
Source key (FROM_ACCT_ID) references SQLCL_BANK_ACCTS
Destination key (TO_ACCT_ID) references SQLCL_BANK_ACCTS
Label TRANSFER
Properties (FROM_ACCT_ID, TO_ACCT_ID, AMOUNT)
);
commit;
SQL> pgql auto on
PGQL Auto enabled for schema=[null], graph=[null], execute=[true], translate=[false]
PGQL> create property graph sqlcl_bank_graph
2 vertex tables (
3 SQLCL_BANK_ACCTS
4 Key (ACCT_ID)
5 Label ACCOUNT
6 Properties (ACCT_ID)
7 )
8 edge tables (
9 SQLCL_BANK_TXNS
10 key(TXN_ID)
11 Source key (FROM_ACCT_ID) references SQLCL_BANK_ACCTS
12 Destination key (TO_ACCT_ID) references SQLCL_BANK_ACCTS
13 Label TRANSFER
14 Properties (FROM_ACCT_ID, TO_ACCT_ID, AMOUNT)
15* );
Graph created
PGQL> commit;
コミットが完了しました。
PGQL>
プロパティ・グラフSQLCL_BANK_GRAPH が作成されました。
元記事ではいくつかのPGQLの実行を行なっていますが、本記事では以下の2行のPGQLについて、APEXのレポートのソースにしてみます。
select v.ACCT_ID, count(*) as NumDeposits match (m)-[e]->(v) group by v.ACCT_ID order by NumDeposits desc limit 5;
select s.ACCT_ID, e.AMOUNT, d.ACCT_ID MATCH (s)-[e]->(d) WHERE s.ACCT_ID=1002 or s.ACCT_ID=1001;
レポートを実装するOracle APEXのアプリケーションを作成します。
アプリケーション作成ウィザード を起動します。
アプリケーションの名前をPGQLレポート とします。
それ以外は変更せず、アプリケーションの作成 を実行します。
レポートを作成するアプリケーションの準備ができました。
PGQLを実行し、SQLを取り出します。デフォルトのグラフをsqlcl_bank_graphにして、実際にデータベースで実行されるSQLが表示されるよう、translateモードに変更します。
pgql auto on graph sqlcl_bank_graph translate PGQL> pgql auto on graph sqlcl_bank_graph translate
PGQL Auto enabled for schema=[null], graph=[SQLCL_BANK_GRAPH] , execute=[true], translate=[true]
PGQL>
最初のPGQLを実行します。
赤字が生成されたSQLになります。
PGQL> select v.ACCT_ID, count(*) as NumDeposits match (m)-[e]->(v) group by v.ACCT_ID order by NumDeposits desc limit 5;
SELECT * FROM(SELECT T0$1.T AS "v.ACCT_ID$T",
T0$1.V AS "v.ACCT_ID$V",
T0$1.VN AS "v.ACCT_ID$VN",
T0$1.VT AS "v.ACCT_ID$VT",
7 AS "NumDeposits$T",
to_nchar(COUNT(*),'TM9','NLS_Numeric_Characters=''.,''') AS "NumDeposits$V",
COUNT(*) AS "NumDeposits$VN",
to_timestamp_tz(null) AS "NumDeposits$VT"
FROM (SELECT DISTINCT EID, SVID, DVID,EL FROM "WKSP_APEXDEV".SQLCL_BANK_GRAPHGE$) T0$0,
"WKSP_APEXDEV".SQLCL_BANK_GRAPHVT$ T0$1
WHERE T0$1.K=n'ACCT_ID' AND
T0$0.DVID=T0$1.VID
GROUP BY T0$1.T,
T0$1.V,
T0$1.VN,
T0$1.VT
ORDER BY COUNT(*) DESC NULLS FIRST)
WHERE ROWNUM <= 5;
v.ACCT_ID NumDeposits
____________ ______________
156 20
649 20
430 20
644 19
227 18
PGQL>
生成されたSELECT文をソースとしたレポートのページを作成します。
ページの作成 をクリックします。
クラシック・レポート を選択します。
ページの名前 はPGQL1 、データ・ソース のソース・タイプ としてSQL問合せ を選択し、SQL SELECT文を入力 に、SQLclで生成させたSELECT文 を入力します。
以上で、ページの作成 をクリックします。
ページが作成されるので、実行 します。
PGQLの実行と同じ結果が得られています。表示される列の名称や個数に違いはありますが、これらはAPEX側で調整可能です。
生成されたSQLに表示する行数である5 という数値がハード・コードされています。APEXのレポートを使う場合、この値をページ・アイテムに置き換えることができます。
例としてページ・アイテムP2_ROWS を作成し、表示行数を指定できるようにしてみます。ソースとなるSELECT文を以下に変更します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SELECT * FROM(SELECT T0$1.T AS "v.ACCT_ID$T",
T0$1.V AS "v.ACCT_ID$V",
T0$1.VN AS "v.ACCT_ID$VN",
T0$1.VT AS "v.ACCT_ID$VT",
7 AS "NumDeposits$T",
to_nchar(COUNT(*),'TM9','NLS_Numeric_Characters=''.,''') AS "NumDeposits$V",
COUNT(*) AS "NumDeposits$VN",
to_timestamp_tz(null) AS "NumDeposits$VT"
FROM (SELECT DISTINCT EID, SVID, DVID,EL FROM "WKSP_APEXDEV".SQLCL_BANK_GRAPHGE$) T0$0,
"WKSP_APEXDEV".SQLCL_BANK_GRAPHVT$ T0$1
WHERE T0$1.K=n'ACCT_ID' AND
T0$0.DVID=T0$1.VID
GROUP BY T0$1.T,
T0$1.V,
T0$1.VN,
T0$1.VT
ORDER BY COUNT(*) DESC NULLS FIRST)
WHERE ROWNUM <= :P2_ROWS
ページ・アイテムP2_ROWS のタイプ を数値フィールド とし、動的アクションを作成 します。動的アクションでは、P2_ROWSの値に変更 があったときに、クラシック・レポートのリージョンPGQL1をリフレッシュ します。
リージョンPGQL1 のソース のSQL問合せ を変更し、送信するページ・アイテム にP2_ROWS を指定します。
以上の変更を行いページを実行すると、以下のような動作になります。
続いて、2番目のPGQLをレポートにしてみます。
PGQL> select s.ACCT_ID, e.AMOUNT, d.ACCT_ID MATCH (s)-[e]->(d) WHERE s.ACCT_ID=1002 or s.ACCT_ID=1001;
SELECT T0$1.T AS "s.ACCT_ID$T",
T0$1.V AS "s.ACCT_ID$V",
T0$1.VN AS "s.ACCT_ID$VN",
T0$1.VT AS "s.ACCT_ID$VT",
T0$0.T AS "e.AMOUNT$T",
T0$0.V AS "e.AMOUNT$V",
T0$0.VN AS "e.AMOUNT$VN",
T0$0.VT AS "e.AMOUNT$VT",
T0$2.T AS "d.ACCT_ID$T",
T0$2.V AS "d.ACCT_ID$V",
T0$2.VN AS "d.ACCT_ID$VN",
T0$2.VT AS "d.ACCT_ID$VT"
FROM "WKSP_APEXDEV".SQLCL_BANK_GRAPHGE$ T0$0,
"WKSP_APEXDEV".SQLCL_BANK_GRAPHVT$ T0$1,
"WKSP_APEXDEV".SQLCL_BANK_GRAPHVT$ T0$2
WHERE T0$0.K=n'AMOUNT' AND
T0$1.K=n'ACCT_ID' AND
T0$2.K=n'ACCT_ID' AND
T0$0.SVID=T0$1.VID AND
T0$0.DVID=T0$2.VID AND
(T0$1.VN = 1002 OR T0$1.VN = 1001);
行が選択されていません
PGQL>
s.ACCT_IDに設定している1001 、1002 のどちらも今回のテスト・データには含まれていないため、選択されるデータはありません。
生成されたSELECT文をレポートのソースとして、先ほどと同様にクラシック・レポートのページを作成します。
今回も同様に、1001、1002とハード・コードされている部分をページ・アイテムP3_ACCT_IDS に置き換えます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SELECT T0$1.T AS "s.ACCT_ID$T",
T0$1.V AS "s.ACCT_ID$V",
T0$1.VN AS "s.ACCT_ID$VN",
T0$1.VT AS "s.ACCT_ID$VT",
T0$0.T AS "e.AMOUNT$T",
T0$0.V AS "e.AMOUNT$V",
T0$0.VN AS "e.AMOUNT$VN",
T0$0.VT AS "e.AMOUNT$VT",
T0$2.T AS "d.ACCT_ID$T",
T0$2.V AS "d.ACCT_ID$V",
T0$2.VN AS "d.ACCT_ID$VN",
T0$2.VT AS "d.ACCT_ID$VT"
FROM "WKSP_APEXDEV".SQLCL_BANK_GRAPHGE$ T0$0,
"WKSP_APEXDEV".SQLCL_BANK_GRAPHVT$ T0$1,
"WKSP_APEXDEV".SQLCL_BANK_GRAPHVT$ T0$2
WHERE T0$0.K=n'AMOUNT' AND
T0$1.K=n'ACCT_ID' AND
T0$2.K=n'ACCT_ID' AND
T0$0.SVID=T0$1.VID AND
T0$0.DVID=T0$2.VID AND
(T0$1.VN in (
select column_value from table(apex_string.split(:P3_ACCT_IDS,','))
)
);
ページ・アイテムP3_ACCT_IDS を作成します。識別 のタイプ はテキスト・フィールド 、ラベル はACCT_IDs とします。ページ・アイテムP3_ACCT_IDS の値に変更 があったときに、リージョンPGQL2をリフレッシュ する動的アクションを作成します。
リージョンPGQL2 のソース のSQL問合せ を置き換えます。送信するページ・アイテム としてP3_ACCT_IDS を指定します。
以上の変更を行いページを実行すると、以下のような動作になります。
今回の作業は以上で完了です。
実際のところ、以下のPGQLのSELECT文は
select v.ACCT_ID, count(*) as NumDeposits match (m)-[e]->(v) group by v.ACCT_ID order by NumDeposits desc limit 5; 以下のSELECT文と同じです。
select to_acct_id, count(*) from sqlcl_bank_txns group by to_acct_id order by 2 desc fetch first 5 rows only; 以下のPGQLのSELECT文は
select s.ACCT_ID, e.AMOUNT, d.ACCT_ID MATCH (s)-[e]->(d) WHERE s.ACCT_ID=1002 or s.ACCT_ID=1001; 以下のSELECT文と同じです。select * from sqlcl_bank_txns where from_acct_id in (1001,1002);
結局のところSQLでもできるのだから(2層モードの場合 - 3層ではSQLでできないこともあります)、プロパティ・グラフとして扱う意味は何だろう?と考えるかもしれません。
Oracle APEXでアプリケーションを作成する上で、一番難しいのはAPEXの部分ではなくリレーショナル・データとしてのモデリングだといえます。プロパティ・グラフで保存することがで自然であれば、無理にリレーショナル形式に落とし込まずにグラフとして保存してもよいでしょう。SQLclがPGQLをSQLに変換してくれるのであれば、グラフからリレーショナルへの形式変換は自動的に行われ、レポートを作成するときに頭を悩ませる必要は無くなります。
簡単なアプリケーションですが、今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/pgql-reports.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完