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_TXNSを作成します。
この後に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文を以下に変更します。
ページ・アイテム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に置き換えます。
ページ・アイテム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のアプリケーション作成の参考になれば幸いです。
完