5 プロパティ・グラフ問合せ言語(PGQL)
PGQLは、頂点からなり、エッジにより別の頂点に接続され、それぞれが関連付けられたキー値ペア(プロパティ)を持つことができる、プロパティ・グラフ・データ構造のためのSQLライクな問合せ言語です。
この言語はグラフ・パターン一致の概念に基づき、データ・グラフの頂点およびエッジに対し一致するパターンを指定することができます。
プロパティ・グラフ・サポートでは、Java APIを介してプロパティ・グラフ問合せ言語(PGQL)の問合せを実行するための2つの方法が提供されています。
-
インメモリー・グラフ・サーバー(PGX)の使用で説明されているように、
oracle.pgx.api
Javaパッケージを使用して、インメモリー・アナリスト(PGX)にロードされているグラフのインメモリー・スナップショットを問い合せます。 -
Oracle Databaseに対して直接PGQL問合せを実行で説明されているように、
oracle.pg.rdbms.pgql
Javaパッケージを使用して、Oracle Databaseに格納されているグラフ・データを直接問い合せます。
PGQLの詳細は、https://pgql-lang.orgを参照してください。
- PGQLを使用したプロパティ・グラフの作成
- PGQLを使用したプロパティ・グラフ・ビューの作成
- PGQLによるパターン一致
- エッジ・パターンはPGQLによって方向を持つ
- PGQLによる頂点およびエッジ・ラベル
- PGQLによる可変長のパス
- PGQLによる集計およびソート
- インメモリー・グラフ・サーバー(PGX)に対するPGQL問合せの実行
このセクションでは、インメモリー・グラフ・サーバー(PGX)でPGQL問合せを実行するために使用されるJava APIについて説明します。 - Oracle Databaseに対して直接PGQL問合せを実行
このトピックでは、(インメモリーと対照的に)Oracle Database内のグラフに対して直接PGQL問合せを実行する方法について説明します。
5.1 PGQLを使用したプロパティ・グラフの作成
CREATE PROPERTY GRAPHは、データベース表からグラフを作成するPGQL DDL文です。グラフはプロパティ・グラフ・スキーマに格納されます。
CREATE PROPERTY GRAPH文は、グラフに付ける名前で始まり、その後に頂点表とエッジ表のセットが続きます。グラフには、頂点表またはエッジ表がない(空のグラフ)か、頂点表がありエッジ表はない(頂点のみでエッジなしのグラフ)か、頂点表とエッジ表の両方がある(頂点とエッジのあるグラフ)場合があります。ただし、エッジ表のみで頂点表のないグラフは指定できません。
次のケースについて検討します。
- PERSONSは、ID列、NAME列およびACCOUNT_NUMBER列がある表です。この表には、アカウントを持つすべての人の行が追加されます。
- TRANSACTIONSは、FROM_ACCOUNT列、TO_ACCOUNT列、DATE列、AMOUNT列がある表です。FROM_ACCOUNTからTO_ACCOUNTに送金されるたびに、データベース内のこの表に行が追加されます。
表からグラフへの簡単なマッピングは次のようになります。マップされたグラフの概念は、頂点、エッジ、ラベル、プロパティです。
- 頂点表: データ・エンティティを含む表は頂点表です。
- 頂点表の各行は頂点です。
- 頂点表の列は、頂点のプロパティです。
- 頂点表の名前は、この頂点セットのデフォルト・ラベルです。あるいは、CREATE PROPERTY GRAPH文の一部としてラベル名を指定できます。
- エッジ表: エッジ表は、2つの頂点表をリンクする任意の表、またはソース・エンティティからターゲット・エンティティへのアクションを示すデータを含む表です。たとえば、FROM_ACCOUNTからTO_ACCOUNTへの送金は自然なエッジです。
- 外部キー関係により、データ内で関連しているリンクを知ることができます。CREATE PROPERTY GRAPHは、デフォルトで外部キー関係を使用してエッジを識別します。
- エッジ表のプロパティの一部は、エッジのプロパティにできます。たとえば、FROM_ACCOUNTからTO_ACCOUNTへのエッジには、DATEおよびAMOUNTプロパティを指定できます。
- エッジ表の名前は、このエッジ・セットのデフォルト・ラベルです。あるいは、CREATE PROPERTY GRAPH文の一部としてラベル名を指定できます。
- キー:
- 頂点表のキー: 頂点表のキーは、グラフ内の一意の頂点を識別します。キーはCREATE PROPERTY GRAPH文で指定できます。指定しない場合、デフォルトで表の主キーになります。表に重複した行がある場合は、CREATE PROPERTY GRAPH文によってエラーが返されます。
- エッジ表のキー: エッジ表のキーは、グラフ内のエッジを一意に識別します。ソースと宛先の頂点を指定すると、KEY句はソースと宛先の頂点を一意に識別します。
次に、PERSONS表とTRANSACTIONS表のCREATE PROPERTY GRAPH文の例を示します。
CREATE PROPERTY GRAPH bank_transfers VERTEX TABLES (persons KEY(account_number)) EDGE TABLES( transactions KEY (from_acct, to_acct, date, amount) SOURCE KEY (from_account) REFERENCES persons DESTINATION KEY (to_account) REFERENCES persons PROPERTIES (date, amount) )
- 表の別名: 頂点表とエッジ表には一意の名前が付いている必要があります。同じリレーショナル表から複数の頂点表を識別したり、同じリレーショナル表から複数のエッジ表を識別する必要がある場合は、別名を使用する必要があります。たとえば、次の例に示すように、1つの表PERSONSから2つの頂点表PERSONSとPERSONS_IDを作成できます。
CREATE PROPERTY GRAPH bank_transfers VERTEX TABLES (persons KEY(account_number) persons_id AS persons KEY(id))
- REFERENCES句: エッジのソースおよび宛先の頂点を、対応する頂点表に接続します。
詳細は、https://pgql-lang.org/spec/latest/#creating-a-property-graphを参照してください。
親トピック: プロパティ・グラフ問合せ言語(PGQL)
5.2 PGQLを使用したプロパティ・グラフ・ビューの作成
リレーショナル・データベース表にプロパティ・グラフ・ビューを作成できます。
プロパティ・グラフ・ビューを作成するには、PGQLのCREATE PROPERTY GRAPH
文を使用します。
プロパティ・グラフ・ビューの作成に使用するCREATE PROPERTY GRAPH
文の例は、例2-1を参照してください。
oracle.pg.rdbms.pgql
パッケージのJava APIは、PGQL問合せの実行をサポートしますが、いくつかの例外があります。
SQLclを使用している場合、プロパティ・グラフ・ビューの作成はサポートされません。ただし、作成したプロパティ・グラフ・ビューは、SQLclのPGQL SELECT
文を使用して問い合せることができます。Python APIまたはグラフ・ビジュアライゼーション・ツールを使用している場合、プロパティ・グラフ・ビューの作成と問合せは両方ともサポートされません。
また、次のPGQL SELECT
機能もサポートされません。
- 再帰的問合せ
- 副問合せ
in_degree
関数とout_degree
関数- バインド変数
親トピック: プロパティ・グラフ問合せ言語(PGQL)
5.3 PGQLによるパターン一致
パターン一致は、MATCH句で1つ以上のパス・パターンを指定することで行われます。単一パス・パターンは頂点およびエッジの線形パスと一致しますが、より複雑なパターンは、複数のパス・パターンをカンマで区切って組み合せることによって一致させることができます。(対応するSQL文に類似した)値式はWHERE句で指定され、通常、頂点およびエッジのプロパティに制約を指定することによって一致をフィルタ処理で除外できます
たとえば、コンピュータ・ネットワーク上のTCP/IP接続のグラフを想定し、誰かが1つのマシンにログインし、そこから別のマシンへ、そこからさらに別のマシンへという場合を検出するとします。次のようなパターンを問合せます。
SELECT id(host1) AS id1, id(host2) AS id2, id(host3) AS id3 /* choose what to return */
FROM MATCH
(host1) -[connection1]-> (host2) -[connection2]-> (host3) /* single linear path pattern to match */
WHERE
connection1.toPort = 22 AND connection1.opened = true AND
connection2.toPort = 22 AND connection2.opened = true AND
connection1.bytes > 300 AND /* meaningful amount of data was exchanged */
connection2.bytes > 300 AND
connection1.start < connection2.start AND /* second connection within time-frame of first */
connection2.start + connection2.duration < connection1.start + connection1.duration
GROUP BY id1, id2, id3 /* aggregate multiple matching connections */
パターン一致のその他の例については、PGQL仕様の関連するセクションを参照してください。
親トピック: プロパティ・グラフ問合せ言語(PGQL)
5.4 エッジ・パターンはPGQLによって方向を持つ
エッジ・パターンは、グラフのエッジと同様に方向を持ちます。そのため、(a) <-[]- (b)
は、bにはaに向けられたエッジがある場合を指定するのに対し、(a) -[]-> (b)
は、逆の方向のエッジを探します。
次の例は、AprilとChrisの共通の友人で両者よりも年上の人を探します。
SELECT friend.name, friend.dob
FROM MATCH /* note the arrow directions below */
(p1:person) -[:likes]-> (friend) <-[:likes]- (p2:person)
WHERE
p1.name = 'April' AND p2.name ='Chris' AND
friend.dob > p1.dob AND friend.dob > p2.dob
ORDER BY friend.dob DESC
エッジ・パターンのその他の例については、こちらのPGQL仕様の関連するセクションを参照してください。
親トピック: プロパティ・グラフ問合せ言語(PGQL)
5.5 PGQLによる頂点およびエッジ・ラベル
ラベルは、グラフのエッジおよびノードにタイプ情報を添付する方法で、すべてのノードが同じものを表しているわけではないグラフの制約に使用することができます。次に例を示します。
SELECT p.name
FROM MATCH (p:person) -[e1:likes]-> (m1:movie),
MATCH (p) -[e2:likes]-> (m2:movie)
WHERE m1.title = 'Star Wars'
AND m2.title = 'Avatar'
ラベル式のその他の例については、こちらのPGQL仕様の関連するセクションを参照してください。
親トピック: プロパティ・グラフ問合せ言語(PGQL)
5.6 PGQLによる可変長のパス
可変長パス・パターンは、頂点およびエッジの変数と一致するように*のような数量詞を持ちます。PATHマクロを使用すると、名前を参照することにより、MATCH句に任意の回数だけ埋め込むことのできる問合せの開始時に、名前付きパス・パターンを指定できます。次の例は、MarioとLuigiの共通の祖先をすべて検索します。
PATH has_parent AS () -[:has_father|has_mother]-> ()
SELECT ancestor.name
FROM MATCH (p1:Person) -/:has_parent*/-> (ancestor:Person)
, MATCH (p2:Person) -/:has_parent*/-> (ancestor)
WHERE
p1.name = 'Mario' AND
p2.name = 'Luigi'
追加の制約または問合せ結果では使用されない、中間エッジまたはノードの名前は定義する必要がないため、前のパス仕様では、匿名制約の使用も示しています。匿名要素は、[:has_father|has_mother]
などの制約を持つことができます。エッジは変数名を取得できません(他の場所で参照されないため)が、制約はされます。
可変長のパス・パターン一致のその他の例については、こちらのPGQL仕様の関連するセクションを参照してください。
親トピック: プロパティ・グラフ問合せ言語(PGQL)
5.7 PGQLによる集計およびソート
SQLのように、PGQLは次のサポートがあります。
-
ソリューションのグループを作成するGROUP BY
-
MIN、MAX、SUMおよびAVG集計
-
結果をソートするORDER BY
および、その他の多くのなじみのあるSQL構造がサポートされます。
GROUP BYおよび集計については、こちらのPGQL仕様の関連するセクションを参照してください。ORDER BYについては、こちらのPGQL仕様の関連するセクションを参照してください。
親トピック: プロパティ・グラフ問合せ言語(PGQL)
5.8 インメモリー・グラフ・サーバー(PGX)に対するPGQL問合せの実行
このセクションでは、インメモリー・グラフ・サーバー(PGX)でPGQL問合せを実行するために使用されるJava APIについて説明します。
- PGQLスタート・ガイド
- サポートされているPGQL機能
インメモリー・グラフ・サーバー(PGX)は、DROP PROPERTY GRAPH
を除くすべてのPGQL機能をサポートしています。 - CREATE PROPERTY GRAPH文を実行するためのJava API
- SELECT問合せを実行するためのJava API
このセクションでは、インメモリー・グラフ・サーバー(PGX)でSELECT
問合せを実行するためのAPIについて説明します。 - UPDATE問合せを実行するためのJava API
UPDATE
問合せは、PGQL 1.3仕様のグラフの変更のセクションで説明されているように、INSERT
、UPDATE
およびDELETE
操作を使用して既存のグラフに変更を加えます。 - PGQL問合せを実行するためのセキュリティ・ツール
問合せインジェクションから保護するために、リテラルのかわりにバインド変数を使用し、グラフ名、ラベル、プロパティ名などの識別子のかわりにprintIdentifier(String identifier)
を使用できます。 - PGQL問合せをチューニングするためのベスト・プラクティス
このセクションでは、メモリー割当て、並列度および問合せ計画に関するベスト・プラクティスについて説明します。
親トピック: プロパティ・グラフ問合せ言語(PGQL)
5.8.1 PGQLスタート・ガイド
このセクションでは、PGQLの開始方法の例を示します。事前に設定されているデータベース・レルムを想定しています(3.1.1「データベース認証のためのグラフ・サーバーの準備」のステップに従ってください)。また、ユーザーがHRスキーマへのread
アクセス権を持っていることも想定しています。
最初に、CREATE PROPERTY GRAPH
文を実行して、従業員、部門およびemployee works at department
を含むグラフを作成します。
例5-1 インメモリー・グラフ・サーバー(PGX)でのグラフの作成
次の文は、インメモリー・グラフ・サーバー(PGX)にグラフを作成します
String statement =
"CREATE PROPERTY GRAPH hr_simplified "
+ " VERTEX TABLES ( "
+ " hr.employees LABEL employee "
+ " PROPERTIES ARE ALL COLUMNS EXCEPT ( job_id, manager_id, department_id ), "
+ " hr.departments LABEL department "
+ " PROPERTIES ( department_id, department_name ) "
+ " ) "
+ " EDGE TABLES ( "
+ " hr.employees AS works_at "
+ " SOURCE KEY ( employee_id ) REFERENCES employees "
+ " DESTINATION departments "
+ " PROPERTIES ( employee_id ) "
+ " )";
session.executePgql(statement);
/**
* To get a handle to the graph, execute:
*/
PgxGraph g = session.getGraph("HR_SIMPLIFIED");
/**
* You can use this handle to run PGQL queries on this graph.
* For example, to find the department that “Nandita Sarchand” works for, execute:
*/
String query =
"SELECT dep.department_name "
+ "FROM MATCH (emp:Employee) -[:works_at]-> (dep:Department) "
+ "WHERE emp.first_name = 'Nandita' AND emp.last_name = 'Sarchand' "
+ "ORDER BY 1";
PgqlResultSet resultSet = g.queryPgql(query);
resultSet.print();
+-----------------+
| department_name |
+-----------------+
| Shipping |
+-----------------+
/**
* To get an overview of the types of vertices and their frequencies, execute:
*/
String query =
"SELECT label(n), COUNT(*) "
+ "FROM MATCH (n) "
+ "GROUP BY label(n) "
+ "ORDER BY COUNT(*) DESC";
PgqlResultSet resultSet = g.queryPgql(query);
resultSet.print();
+-----------------------+
| label(n) | COUNT(*) |
+-----------------------+
| EMPLOYEE | 107 |
| DEPARTMENT | 27 |
+-----------------------+
/**
*To get an overview of the types of edges and their frequencies, execute:
*/
String query =
"SELECT label(n) AS srcLbl, label(e) AS edgeLbl, label(m) AS dstLbl, COUNT(*) "
+ "FROM MATCH (n) -[e]-> (m) "
+ "GROUP BY srcLbl, edgeLbl, dstLbl "
+ "ORDER BY COUNT(*) DESC";
PgqlResultSet resultSet = g.queryPgql(query);
resultSet.print();
+---------------------------------------------+
| srcLbl | edgeLbl | dstLbl | COUNT(*) |
+---------------------------------------------+
| EMPLOYEE | WORKS_AT | DEPARTMENT | 106 |
+---------------------------------------------+
5.8.2 サポートされているPGQL機能
インメモリー・グラフ・サーバー(PGX)は、DROP PROPERTY GRAPH
を除くすべてのPGQL機能をサポートしています。
次に説明されている特定の制限がある機能はほとんどありません。
- 数量詞の制限
到達可能性パターンでは、*
、+
、{1,4}
などのすべての数量詞がサポートされていますが、最短で最小コストのパス・パターンでサポートされている数量詞は*
(ゼロ以上)のみです。 - 定量化されたパターンでのWHERE句およびCOST句の制限事項
5.8.2.1 数量詞の制限
到達可能性パターンでは、*
、+
、{1,4}
などのすべての数量詞がサポートされていますが、最短で最小コストのパス・パターンでサポートされている数量詞は*
(ゼロ以上)のみです。
親トピック: サポートされているPGQL機能
5.8.2.2 定量化されたパターンでのWHERE句およびCOST句の制限事項
到達可能性パターンや最短パス・パターンおよび最小コスト・パス・パターンなどの定量化されたパターンのWHERE
句およびCOST
句は、単一の変数の参照のみに制限されます。
次に、WHERE
句またはCOST
句が、zero
またはone
のかわりに2つの変数e
およびx
を参照しているためにサポートされていない問合せの例を示します。
... PATH p AS (n) –[e]-> (m) WHERE e.prop > m.prop ...
... SHORTEST ( (n) (-[e]-> (x) WHERE e.prop + x.prop > 10)* (m) ) ...
... CHEAPEST ( (n) (-[e]-> (x) COST e.prop + x.prop )* (m) ) ...
次の問合せがサポートされているのは、副問合せは外部スコープから単一の変数a
のみを参照し、変数c
は副問合せで新たに導入されたためにカウントされないためです。
... PATH p AS (a) -> (b)
WHERE EXISTS ( SELECT * FROM MATCH (a) -> (c) ) ...
親トピック: サポートされているPGQL機能
5.8.3 CREATE PROPERTY GRAPH文を実行するためのJava API
CREATE PROPERTY GRAPH
文を実行する最も簡単な方法は、PgxSession.executePgql(String statement)
メソッドを使用することです。
例5-2 CREATE PROPERTY GRAPH文の実行
String statement =
"CREATE PROPERTY GRAPH hr_simplified "
+ " VERTEX TABLES ( "
+ " hr.employees LABEL employee "
+ " PROPERTIES ARE ALL COLUMNS EXCEPT ( job_id, manager_id, department_id ), "
+ " hr.departments LABEL department "
+ " PROPERTIES ( department_id, department_name ) "
+ " ) "
+ " EDGE TABLES ( "
+ " hr.employees AS works_at "
+ " SOURCE KEY ( employee_id ) REFERENCES employees "
+ " DESTINATION departments "
+ " PROPERTIES ( employee_id ) "
+ " )";
session.executePgql(statement);
PgxGraph g = session.getGraph("HR_SIMPLIFIED");
/**
* Alternatively, one can use the prepared statement API, for example:
*/
PgxPreparedStatement stmnt = session.preparePgql(statement);
stmnt.execute();
stmnt.close();
PgxGraph g = session.getGraph("HR_SIMPLIFIED");
5.8.4 SELECT問合せを実行するためのJava API
このセクションでは、インメモリー・グラフ・サーバー(PGX)でSELECT
問合せを実行するためのAPIについて説明します。
- インメモリー・グラフ・サーバー(PGX)のグラフに対するSELECT問合せの実行
PgxGraph.queryPgql(String query)
メソッドは、PgxGraphの作成に使用されたセッションで問合せを実行します。このメソッドは、PgqlResultSet
を返します。 - PGXセッションに対するSELECT問合せの実行
PgxSession.queryPgql(String query)
メソッドは、セッションで指定された問合せを実行し、PgqlResultSet
を返します。 - 結果セットの反復処理
結果セットを反復処理するには、JDBCに似た方法またはJava Iteratorインタフェースを使用する方法があります。 - 結果セットの出力
PgqlResultSet (パッケージoracle.pgx.api)
の次のメソッドは、結果セットを出力するために使用されます。
5.8.4.1 インメモリー・グラフ・サーバー(PGX)のグラフに対するSELECT問合せの実行
PgxGraph.queryPgql(String query)
メソッドは、PgxGraphの作成に使用されたセッションで問合せを実行します。このメソッドは、PgqlResultSet
を返します。
問合せはPGXグラフに対して直接実行されるため、MATCH
句内のON
句は省略できます。同じ理由で、INSERT
句内のINTO
句は省略できます。ただし、ON
句およびINTO
句でグラフ名を明示的に指定する場合、これらのグラフ名がグラフの実際の名前(PgxGraph.getName()
)と一致している必要があります。
親トピック: SELECT問合せを実行するためのJava API
5.8.4.2 PGXセッションに対するSELECT問合せの実行
PgxSession.queryPgql(String query)
メソッドは、セッションで指定された問合せを実行し、PgqlResultSet
を返します。
MATCH
句内のON
句、およびINSERT
句内のINTO
句は、指定する必要があり、省略できません。この時点では、単一の問合せで複数のグラフのデータを結合することはまだサポートされていないため、問合せのすべてのON
句およびINTO
句で同じグラフを参照する必要があります。
親トピック: SELECT問合せを実行するためのJava API
5.8.4.3 結果セットの反復処理
結果セットを反復処理するには、JDBCに似た方法またはJava Iteratorインタフェースを使用する方法があります。
JDBCのような反復の場合、PgqlResultSet
(パッケージoracle.pgx.api
)のメソッドはjava.sql.ResultSet
のメソッドと似ています。主な違いは、PGQLの結果セット・インタフェースはJava 8で導入された新しい日時ライブラリに基づいているのに対し、java.sql.ResultSet
はレガシーjava.util.Date
に基づいていることです。ギャップを埋めるために、PGQLの結果セットは、java.util.Date
をまだ使用しているアプリケーションにgetLegacyDate(..)
を提供します。
PgqlResultSet
には、最初の行の前に初期設定されるcursor
があります。その後、次のメソッドを使用してカーソルを再配置できます。
next() : boolean
previous() : boolean
beforeFirst()
afterLast()
first() : boolean
last() : boolean
absolute(long row) : boolean
relative(long rows) : boolean
前述のメソッドの詳細は、ここを参照してください。
getObject(int columnIdx) : Object
getObject(String columnName) : Object
getString(int columnIdx) : String
getString(String columnName) : String
getInteger(int columnIdx) : Integer
getInteger(String columnName) : Integer
getLong(int columnIdx) : Long
getLong(String columnName) : Long
getFloat(int columnIdx) : Float
getFloat(String columnName) : Float
getDouble(int columnIdx) : Double
getDouble(String columnName) : Double
getBoolean(int columnIdx) : Boolean
getBoolean(String columnName) : Boolean
getVertexLabels(int columnIdx) : Set<String>
getVertexLabels(String columnName) : Set<String>
getDate(int columnIdx) : LocalDate
getDate(String columnName) : LocalDate
getTime(int columnIdx) : LocalTime
getTime(String columnName) : LocalTime
getTimestamp(int columnIdx) : LocalDateTime
getTimestamp(String columnName) : LocalDateTime
getTimeWithTimezone(int columnIdx) : OffsetTime
getTimeWithTimezone(String columnName) : OffsetTime
getTimestampWithTimezone(int columnIdx) : OffsetDateTime
getTimestampWithTimezone(String columnName) : OffsetDateTime
getLegacyDate(int columnIdx) : java.util.Date
getLegacyDate(String columnName) : java.util.Date
getList(int columnIdx) : List<T>
getList(String columnName) : List<T>
前述のメソッドの詳細は、ここを参照してください。
最後に、結果セットのリソースを解放するPgqlResultSet.close()
があり、PgqlResultSet.getMetaData()
を使用して列名と列数を取得できます。
結果セットの反復の例は次のとおりです。
PgqlResultSet resultSet = g.queryPgql(
" SELECT owner.name AS account_holder, SUM(t.amount) AS total_transacted_with_Nikita "
+ " FROM MATCH (p:Person) -[:ownerOf]-> (account1:Account) "
+ " , MATCH (account1) -[t:transaction]- (account2) "
+ " , MATCH (account2:Account) <-[:ownerOf]- (owner:Person|Company) "
+ " WHERE p.name = 'Nikita' "
+ " GROUP BY owner");
while (resultSet.next()) {
String accountHolder = resultSet.getString(1);
long totalTransacted = resultSet.getLong(2);
System.out.println(accountHolder + ": " + totalTransacted);
}
resultSet.close();
前述の例の出力は次のようになります。
Oracle: 4501
Camille: 1000
さらに、PgqlResultSet
もJava Iteratorインタフェースを介して反復可能です。結果セットに対する"for each loop"の例は、次のとおりです。
for (PgxResult result : resultSet) {
String accountHolder = result.getString(1);
long totalTransacted = result.getLong(2);
System.out.println(accountHolder + ": " + totalTransacted);
}
前述の例の出力は次のようになります。
Oracle: 4501
Camille: 1000
PgqlResultSet
で使用可能なものと同じgetterをPgxResult
でも使用できることに注意してください。
親トピック: SELECT問合せを実行するためのJava API
5.8.4.4 結果セットの出力
PgqlResultSet (パッケージoracle.pgx.api)
の次のメソッドは、結果セットを出力するために使用されます。
print() : PgqlResultSet
print(long numResults) : PgqlResultSet
print(long numResults, int from) : PgqlResultSet
print(PrintStream printStream, long numResults, int from) : PgqlResultSet
次に例を示します。
g.queryPgql("SELECT COUNT(*) AS numPersons FROM MATCH (n:Person)").print().close()
+------------+
| numPersons |
+------------+
| 3 |
+------------+
もう1つ例を示します。
PgqlResultSet resultSet = g.queryPgql(
" SELECT owner.name AS account_holder, SUM(t.amount) AS total_transacted_with_Nikita "
+ " FROM MATCH (p:Person) -[:ownerOf]-> (account1:Account) "
+ " , MATCH (account1) -[t:transaction]- (account2) "
+ " , MATCH (account2:Account) <-[:ownerOf]- (owner:Person|Company) "
+ " WHERE p.name = 'Nikita' "
+ " GROUP BY owner")
resultSet.print().close()
+-----------------------------------------------+
| account_holder | total_transacted_with_Nikita |
+-----------------------------------------------+
| Camille | 1000.0 |
| Oracle | 4501.0 |
+-----------------------------------------------+
親トピック: SELECT問合せを実行するためのJava API
5.8.5 UPDATE問合せを実行するためのJava API
UPDATE
問合せは、PGQL 1.3仕様のグラフの変更のセクションで説明されているように、INSERT
、UPDATE
およびDELETE
操作を使用して既存のグラフに変更を加えます。
INSERT
を使用するとグラフに新しい頂点およびエッジを挿入でき、UPDATE
を使用するとプロパティを新しい値に設定して既存の頂点およびエッジを更新でき、DELETE
を使用するとグラフから頂点およびエッジを削除できます。
- インメモリー・グラフ・サーバー(PGX)のグラフに対するUPDATE問合せの実行
グラフに対してUPDATE
問合せを実行するには、PgxGraph.executePgql(String query)
メソッドを使用します。 - PGXセッションに対するUPDATE問合せの実行
現時点では、PgxSession
に対するUPDATE
問合せの実行はサポートされていないため、更新は常にPgxGraphに対して実行する必要があります。セッションからグラフを取得するには、PgxSession.getGraph(String graphName)
メソッドを使用します。 - PGQLによるグラフの更新可能性
Oracle RDBMSまたはCSVファイルからPGXにロードされたグラフ・データは、PGQLを介してすぐに更新することはできません。 - グラフの基礎となるスキーマの変更
INSERT
操作では、既知のラベルとプロパティを持つ頂点とエッジのみを挿入できます。同様に、UPDATE
操作では、既知のプロパティの値のみを設定できます。したがって、新しいデータは常にグラフの既存のスキーマに準拠する必要があります。
5.8.5.1 インメモリー・グラフ・サーバー(PGX)のグラフに対するUPDATE問合せの実行
グラフに対してUPDATE
問合せを実行するには、PgxGraph.executePgql(String query)
メソッドを使用します。
次に、INSERT
問合せの例を示します。
g.executePgql("INSERT VERTEX v " +
" LABELS ( Person ) " +
" PROPERTIES ( v.firstName = 'Camille', " +
" v.lastName = ' Mullins' ) ");
INSERT
のINTO
句は省略できることに注意してください。INTO
句を使用する場合、INTO
句のグラフ名は、問合せが実行されるPGXグラフ(PgxGraph.getName()
)の名前に対応している必要があります。
次に、UPDATE
問合せの例を示します。
// set the date of birth of Camille to 2014-11-15
g.executePgql("UPDATE v SET ( v.dob = DATE '2014-11-14' ) " +
"FROM MATCH (v:Person) " +
"WHERE v.firstName = 'Camille' AND v.lastName = ' Mullins' ");
次に、DELETE
問合せの例を示します。
// delete Camille from the graph
g.executePgql("DELETE v " +
"FROM MATCH (v:Person) " +
"WHERE v.firstName = 'Camille' AND v.lastName = 'Mullins' ");
親トピック: UPDATE問合せを実行するためのJava API
5.8.5.2 PGXセッションに対するUPDATE問合せの実行
現時点では、PgxSession
に対するUPDATE
問合せの実行はサポートされていないため、更新は常にPgxGraphに対して実行する必要があります。セッションからグラフを取得するには、PgxSession.getGraph(String graphName)
メソッドを使用します。
親トピック: UPDATE問合せを実行するためのJava API
5.8.5.3 PGQLによるグラフの更新可能性
Oracle RDBMSまたはCSVファイルからPGXにロードされたグラフ・データは、PGQLを介してすぐに更新することはできません。
最初に、PgxGraph.clone()
メソッドを使用してデータのコピーを作成する必要があります。結果のグラフは完全に更新可能です。
次のケースについて検討します。
// load a graph from the RDBMS or from CSV
PgxGraph g1 = session.readGraphWithProperties("path/to/graph_config.json");
// create an updatable copy of the graph
PgxGraph g2 = g1.clone("new_graph_name");
// insert an additional vertex into the graph
g2.executePgql("INSERT VERTEX v " +
" LABELS ( Person ) " +
" PROPERTIES ( v.firstName = 'Camille', " +
" v.lastName = ' Mullins')");
さらに、前述の例の最後の2つのステップを組み合せて単一のステップにするPgxGraph.cloneAndExecutePgql(String query, String graphName)
メソッドもあります。
// create an updatable copy of the graph while inserting a new vertex
PgxGraph g2_copy = g1.cloneAndExecutePgql(
"INSERT VERTEX v " +
" LABELS ( Person ) " +
" PROPERTIES ( v.firstName = 'Camille', " +
" v.lastName = ' Mullins') "
, "new_graph_name");
PgxGraph.clone()
を介して作成されるグラフは、セッションに対してローカルであることに注意してください。ただし、PgxGraph.publish(..)
メソッドを介して他のセッションと共有することはできますが、PGQLを使用して更新することはできなくなりました。セッションローカル・グラフのみが更新可能ですが、永続グラフは更新できません。
親トピック: UPDATE問合せを実行するためのJava API
5.8.5.4 グラフの基礎となるスキーマの変更
INSERT
操作では、既知のラベルとプロパティを持つ頂点とエッジのみを挿入できます。同様に、UPDATE
操作では、既知のプロパティの値のみを設定できます。したがって、新しいデータは常にグラフの既存のスキーマに準拠する必要があります。
ただし、グラフのスキーマを更新するためのPGX APIはいくつか存在します。新しいラベルを追加するためのAPIは存在しませんが、PgxGraph.createVertexProperty(PropertyType type, String name)
およびPgxGraph.createEdgeProperty(PropertyType type, String name)
メソッドを介して新しいプロパティを追加できます。新しいプロパティは、ラベルに関係なく、グラフ内の各頂点/エッジにアタッチされます。最初はプロパティにデフォルト値が割り当てられますが、その後UPDATE
文を介して値を更新できます。
次のケースについて検討します。
// load a graph from the RDBMS or from CSV
PgxGraph g = session.readGraphWithProperties("path/to/graph_config.json");
// add a new property to the graph
g.createVertexProperty(PropertyType.LOCAL_DATE, "dob");
// set the date of birth of Camille to 2014-11-15
g.executePgql("UPDATE v SET ( v.dob = DATE '2014-11-14' ) " +
"FROM MATCH (v:Person) " +
"WHERE v.firstName = 'Camille' AND v.lastName = ' Mullins' ");
親トピック: UPDATE問合せを実行するためのJava API
5.8.6 PGQL問合せを実行するためのセキュリティ・ツール
問合せインジェクションから保護するために、リテラルのかわりにバインド変数を使用し、グラフ名、ラベル、プロパティ名などの識別子のかわりにprintIdentifier(String identifier)
を使用できます。
- バインド変数の使用
バインド変数を使用する理由は2つあります。 - 安全な方法での識別子の使用
文字列の連結を介して問合せを作成すると、問合せのリテラルのみでなく、グラフ名、ラベル、プロパティ名などの識別子でもセキュリティ上のリスクが生じます。唯一の問題は、バインド変数がそのような識別子でサポートされていないことです。したがって、これらの識別子がアプリケーションの観点から可変である場合は、oracle.pgql.lang.ir.PgqlUtils.printIdentifier(String identifier)
メソッドを介して識別子を渡すことにより、問合せインジェクションから保護することをお薦めします。
5.8.6.1 バインド変数の使用
バインド変数を使用する理由は2つあります。
- 問合せインジェクションから保護します。
- 問合せの再コンパイルを必要とせずに同じバインド変数を複数回設定できるため、問合せの実行が高速化されます。
プリペアド文を作成するには、次の2つの方法のいずれかを使用します。
PgxGraph.preparePgql(String query) : PgxPreparedStatement
PgxSession.preparePgql(String query) : PgxPreparedStatement
これらのメソッドから返されるPgxPreparedStatement (package oracle.pgx.api)
には、バインド変数を指定されたデータ型の値にバインドするためのsetterメソッドがあります。
PreparedStatement stmnt = g.preparePgql(
"SELECT v.id, v.dob " +
"FROM MATCH (v) " +
"WHERE v.firstName = ? AND v.lastName = ?");
stmnt.setString(1, "Camille");
stmnt.setString(2, "Mullins");
ResultSet rs = stmnt.executeQuery();
問合せ内の各バインド変数は、PgxPreparedStatement
の次のセッターのいずれかを使用して値に設定する必要があります。
setBoolean(int parameterIndex, boolean x)
setDouble(int parameterIndex, double x)
setFloat(int parameterIndex, float x)
setInt(int parameterIndex, int x)
setLong(int parameterIndex, long x)
setDate(int parameterIndex, LocalDate x)
setTime(int parameterIndex, LocalTime x)
setTimestamp(int parameterIndex, LocalDateTime x)
setTimeWithTimezone(int parameterIndex, OffsetTime x)
setTimestampWithTimezone(int parameterIndex, OffsetDateTime x)
setArray(int parameterIndex, List<?> x)
すべてのバインド変数が設定されると、文は次の方法で実行できます。
PgxPreparedStatement.executeQuery()
SELECT
問合せの場合のみ- ResultSetを返します
PgxPreparedStatement.execute()
- あらゆるタイプの文の場合
- 結果の形式を示すブール値を返します。
SELECT
問合せの場合はtrue、それ以外の場合はfalseです。 SELECT
の場合、ResultSetには後でPgxPreparedStatement.getResultSet()
を使用してアクセスできます。
PGQLでは、配列リテラルを含む任意のデータ型のリテラルのかわりにバインド変数を使用できます。文字列配列のインスタンスにバインド変数が設定されている問合せの例は次のとおりです。
List<String> countryNames = new ArrayList<String>();
countryNames.add("Scotland");
countryNames.add("Tanzania");
countryNames.add("Serbia");
PreparedStatement stmnt = g.preparePgql(
"SELECT n.name, n.population " +
"FROM MATCH (c:Country) " +
"WHERE c.name IN ?");
ResultSet rs = stmnt.executeQuery();
最後に、プリペアド文が不要になった場合は、PgxPreparedStatement.close()
を介してクローズされ、リソースが解放されます。
親トピック: PGQL問合せを実行するためのセキュリティ・ツール
5.8.6.2 安全な方法での識別子の使用
文字列の連結を介して問合せを作成すると、問合せのリテラルのみでなく、グラフ名、ラベル、プロパティ名などの識別子でもセキュリティ上のリスクが生じます。唯一の問題は、バインド変数がそのような識別子でサポートされていないことです。したがって、これらの識別子がアプリケーションの観点から可変である場合は、oracle.pgql.lang.ir.PgqlUtils.printIdentifier(String identifier)
メソッドを介して識別子を渡すことにより、問合せインジェクションから保護することをお薦めします。
識別子文字列を指定すると、メソッドは識別子の先頭と末尾に二重引用符を自動的に追加し、識別子内の文字を適切にエスケープします。
次のケースについて検討します。
String graphNamePrinted = printIdentifier("my graph name with \" special % characters ");
PreparedStatement stmnt = g.preparePgql(
"SELECT COUNT(*) AS numVertices FROM MATCH (v) ON " + graphNamePrinted);
親トピック: PGQL問合せを実行するためのセキュリティ・ツール
5.8.7 PGQL問合せをチューニングするためのベスト・プラクティス
このセクションでは、メモリー割当て、並列度および問合せ計画に関するベスト・プラクティスについて説明します。
- メモリー割当て
インメモリー・アナリスト(PGX)にはon-heap
およびoff-heap
メモリーがあり、前者は標準JVMヒープですが、後者はPGXによって管理される別のヒープです。グラフ・データと同様に、PGQL問合せの中間結果および最終結果は、一部がオンヒープおよび一部がオフヒープで格納されます。したがって、両方のヒープが必要です。 - 並列度
デフォルトでは、使用可能なすべてのプロセッサ・スレッドがPGQL問合せの処理に使用されます。ただし、必要に応じてインメモリー・アナリスト(PGX)の並列度オプションを設定することにより、スレッドの数を制限できます。 - 問合せ計画の説明
PgxGraph.explainPgql(String query)
メソッドは、問合せの問合せ計画を把握するために使用されます。このメソッドは、次のメソッドを持つOperation (package oracle.pgx.api)
のインスタンスを返します。
5.8.7.1 メモリー割当て
インメモリー・アナリスト(PGX)にはon-heap
およびoff-heap
メモリーがあり、前者は標準JVMヒープですが、後者はPGXによって管理される別のヒープです。グラフ・データと同様に、PGQL問合せの中間結果および最終結果は、一部がオンヒープおよび一部がオフヒープで格納されます。したがって、両方のヒープが必要です。
オンヒープ・メモリーの場合、JVMの起動時にデフォルトの最大値が選択されますが、-Xmx
オプションを介して上書きできます。
オフヒープの場合、デフォルトでは最大値は設定されず、オフヒープのメモリー使用量はシステム・リソースを使い果たすまで自動的に増加し続けます。システム・リソースがなくなると、操作が取り消され、メモリーが解放され、適切な例外がユーザーに渡されます。必要に応じて、PGXのmax_off_heap_size
オプションを介して最大オフヒープ・サイズを構成できます。
可能なかぎり最大のグラフをロードして問合せを実行できるようにするための適切な開始点として、オンヒープ・とオフヒープの比率を1:1にすることをお薦めします。たとえば、マシンで256GBのメモリーが使用可能な場合、最大オンヒープを125GBに設定すると、同様の量のメモリーがオフヒープに使用可能になります。
export JAVA_OPTS="-Xmx125g"
5.8.7.2 パラレル化
デフォルトでは、使用可能なすべてのプロセッサ・スレッドがPGQL問合せの処理に使用されます。ただし、必要に応じてインメモリー・アナリスト(PGX)の並列度オプションを設定することにより、スレッドの数を制限できます。
5.8.7.3 問合せ計画の説明
PgxGraph.explainPgql(String query)
メソッドは、問合せの問合せ計画を把握するために使用されます。このメソッドは、次のメソッドを持つOperation (package oracle.pgx.api)
のインスタンスを返します。
print()
: 操作とその子操作を出力するためgetOperationType()
: 操作のタイプを取得するためgetPatternInfo()
: 操作の文字列表現を取得するためgetCostEstimate()
: 操作のコストを取得するためgetTotalCostEstimate()
: 操作とその子操作のコストを取得するためgetCardinatlityEstimate()
: 予想される結果行数を取得するためgetChildren()
: 子操作にアクセスするため
次のケースについて検討します。
g.explainPgql("SELECT COUNT(*) FROM MATCH (n) -[e1]-> (m) -[e2]-> (o)").print()
\--- GROUP BY GroupBy {"cardinality":"42", "cost":"42", "accumulatedCost":"58.1"}
\--- (m) -[e2]-> (o) NeighborMatch {"cardinality":"3.12", "cost":"3.12", "accumulatedCost":"16.1"}
\--- (n) -[e1]-> (m) NeighborMatch {"cardinality":"5", "cost":"5", "accumulatedCost":"13"}
\--- (n) RootVertexMatch {"cardinality":"8", "cost":"8", "accumulatedCost":"8"}
前述の例では、print()
メソッドを使用して問合せ計画を出力しています。
問合せ計画が最適でない場合、パフォーマンスを向上させるために問合せを書き直せることがよくあります。たとえば、合計実行時間を改善する方法として、SELECT
問合せをUPDATE
およびSELECT
問合せに分割できます。
インメモリー・アナリスト(PGX)はヒント・メカニズムを提供しないことに注意してください。
5.9 Oracle Databaseに対して直接PGQL問合せを実行
このトピックでは、(インメモリーと対照的に)Oracle Database内のグラフに対して直接PGQL問合せを実行する方法について説明します。
プロパティ・グラフ問合せ言語(PGQL)の問合せは、Oracle Databaseに格納されているディスク常駐プロパティ・グラフ・データに対して実行できます。Oracle Database (RDBMS)上のPGQLには、PGQL問合せを実行するためのJava APIが用意されています。RDBMS上のPGQL内のロジックは発行されたPGQL問合せを同等のSQL問合せに変換し、結果として作成されたSQLがデータベース・サーバーで実行されます。次に、RDBMS上のPGQLは、便利なPGQL結果セットAPIでSQL問合せの結果をラップします。
このPGQL問合せの実行フローは次の図のようになります。
基本的な実行フローは次のとおりです。
-
PGQL問合せはJava APIを介してRDBMS上のPGQLに送信されます。
-
PGQL問合せはSQLに変換されます。
-
変換されたSQLは、JDBCによってOracle Databaseに送信されます。
-
SQL結果セットは、PGQL結果セットとしてラップされてコール元に返されます。
Oracle Databaseに格納されたプロパティ・グラフ・データに対してPGQL問合せを直接実行できることにより、いくつかの利点があります。
-
PGQLでは、VT$、VD$、GE$、およびGT$を含めた問合せスキーマ表に手動で書き込まれるSQLよりも、より自然なグラフ問合せの表現方法が提供されています。
-
PGQL問合せは、グラフ・データのスナップショットをPGXにロードせずに実行できます。そのため、頻繁に更新されるグラフ・データの失効について心配する必要がありません。
-
PGQL問合せは、大きすぎてメモリー内に収まらないグラフ・データに対して実行できます。
-
堅牢でスケーラブルなOracle SQLエンジンを、PGQL問合せの実行に使用できます。
-
Oracle Databaseの管理、監視、およびチューニングを行うための成熟したツールを、PGQL問合せのチューニングと監視に使用できます。
- サポートされるPGQL機能
- CREATE PROPERTY GRAPH文によるプロパティ・グラフの作成
- DROP PROPERTY GRAPH文によるプロパティ・グラフの削除
- oracle.pg.rdbms.pgql Javaパッケージを使用したPGQL問合せの実行
- INSERT、UPDATEおよびDELETE文によるプロパティ・グラフの変更
- PGQL問合せでのパフォーマンスの考慮事項
親トピック: プロパティ・グラフ問合せ言語(PGQL)
5.9.1 サポートされるPGQL機能
PGQLは、プロパティ・グラフ・データの問合せを行うための、SQLに似た問合せ言語です。これは、グラフのパターン一致の概念に基づいており、これを使用して、特にトポロジ制約、パス、フィルタ、ソート、および集計を指定できます。
oracle.pg.rdbms.pgql
パッケージに定義されているPGQL用のJava APIは、いくつかの例外付きでPGQL 仕様をサポートします。(PGQL仕様については、https://pgql-lang.orgを参照してください)。
PGQL の次の機能はサポートされません。
- 最短パス
- ARRAY_AGG集計
- INおよびNOT IN述語
COST
関数を使用した単一のCHEAPESTパスとTOP-K CHEAPESTパス- ラベルとプロパティに対する大文字の参照の大文字と小文字を区別しない一致
さらに、PGQL の次の機能には特別な注意が必要です。
5.9.1.1 Temporal型
Temporal型のDATE、TIMESTAMP、およびTIMESTAMP WITH TIMEZONEがPGQL問合せでサポートされています。
これらのすべての値型は、Oracle SQLのTIMESTAMP WITH TIME ZONE型を使用して内部で表されます。DATEの値は、UTC+0のタイムゾーンで最も早い時刻と想定して、自動的にTIMESTAMP WITH TIME ZONEに変換されます(たとえば、2000-01-01は2000-01-01 00:00:00.00+00:00になります)。TIMESTAMPの値は、UTC+0タイムゾーンと想定して、自動的にTIMESTAMP WITH TIME ZONEに変換されます(たとえば、2000-01-01 12:00:00.00は2000-01-01 12:00:00.00+00:00)。
Temporalの定数は、PGQL問合せに次のように書き込まれます。
-
DATE 'YYYY-MM-DD'
-
TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF'
-
TIMESTAMP WITH TIMEZONE 'YYYY-MM-DD HH24:MI:SS.FFTZH:TZM'
例としては、DATE '2000-01-01'、TIMESTAMP '2000-01-01 14:01:45.23'、TIMESTAMP WITH TIMEZONE '2000-01-01 13:00:00.00-05:00'、およびTIMESTAMP WITH TIMEZONE '2000-01-01 13:00:00.00+01:00'などがあります。
また、Temporal値は、String値をTemporal型にキャストすることで取得できます。サポートされている文字列形式は次のとおりです。
-
DATE 'YYYY-MM-DD'
-
TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF'および'YYYY-MM-DD"T"HH24:MI:SS.FF'
-
TIMESTAMP WITH TIMEZONE 'YYYY-MM-DD HH24:MI:SS.FFTZH:TZM'および'YYYY-MM-DD"T"HH24:MI:SS.FFTZH:TZM'
例としては、CAST ('2005-02-04' AS DATE)、CAST ('1990-01-01 12:00:00.00' AS TIMESTAMP)、CAST ('1985-01-01T14:05:05.00-08:00' AS TIMESTAMP WITH TIMEZONE)などがあります。
PgqlResultSet
オブジェクトからの結果を使用する場合、getObject
はTemporal型のjava.sql.Timestamp
オブジェクトを返します。
バインド変数は、PGQLではTIMESTAMP WITH TIMEZONEのTemporal型にのみ使用でき、java.sql.Timestamp
オブジェクトを入力として取るsetTimestamp
メソッドがバインド値を設定するために使用されます。より単純な別の方法として、CAST文で文字列のバインド変数を使用してTemporal値(たとえば、CAST (? AS TIMESTAMP WITH TIMEZONE)
とそれに続くsetString(1, "1985-01-01T14:05:05.00-08:00")
)をバインドできます。バインド変数の詳細は、「PGQL問合せでのバインド変数の使用」も参照してください。
親トピック: サポートされるPGQL機能
5.9.1.2 型キャスト
型キャストは、SQLスタイルCAST (VALUE AS DATATYPE)構文を使用するPGQLでサポートされます。たとえば、CAST ('25' AS INT)、CAST (10 AS STRING)、CAST ('2005-02-04' AS DATE)、CAST (e.weight AS STRING)などです。サポートされているキャスト操作を次の表にまとめます。Yは、変換がサポートされることを示し、Nは、変換がサポートされないことを示します。無効な値に対するキャスト操作(たとえば、CAST ('xyz' AS INT))またはサポートされない変換(たとえば、CAST (10 AS TIMESTAMP))では、SQL例外ではなく、NULLが返されます。
表5-1 PGQLでの型キャストのサポート(変換元の型および変換先の型)
"変換先"の型 | STRINGから | INTから | LONGから | FLOATから | DOUBLEから | BOOLEANから | DATEから | TIMESTAMPから | TIMESTAMP WITH TIMEZONEから |
---|---|---|---|---|---|---|---|---|---|
STRINGへ |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
INTへ |
Y |
Y |
Y |
Y |
Y |
Y |
N |
N |
N |
LONGへ |
Y |
Y |
Y |
Y |
Y |
Y |
N |
N |
N |
FLOATへ |
Y |
Y |
Y |
Y |
Y |
Y |
N |
N |
N |
DOUBLEへ |
Y |
Y |
Y |
Y |
Y |
Y |
N |
N |
N |
BOOLEANへ |
Y |
Y |
Y |
Y |
Y |
Y |
N |
N |
N |
DATEへ |
Y |
N |
N |
N |
N |
N |
Y |
Y |
Y |
TIMESTAMPへ |
Y |
N |
N |
N |
N |
N |
Y |
Y |
Y |
TIMESTAMP WITH TIMEZONEへ |
Y |
N |
N |
N |
N |
N |
Y |
Y |
Y |
型キャストを使用する問合せ例を次に示します。
SELECT e.name, CAST (e.birthDate AS STRING) AS dob
FROM MATCH (e)
WHERE e.birthDate < CAST ('1980-01-01' AS DATE)
親トピック: サポートされるPGQL機能
5.9.1.3 CONTAINS組込み関数
CONTAINS組込み関数がサポートされています。これは、Oracle Text索引とともに、頂点プロパティとエッジ・プロパティに対して使用されます。CONTAINSは、値がOracle Text検索文字列に一致するとtrue
を返し、一致しないとfalse
を返します。
問合せ例は次のとおりです。
SELECT v.name
FROM MATCH (v)
WHERE CONTAINS(v.abstract, 'Oracle')
PGQLでの全文索引の使用の詳細は、「PGQL問合せでのテキスト索引の使用」も参照してください。
親トピック: サポートされるPGQL機能
5.9.2 CREATE PROPERTY GRAPH文によるプロパティ・グラフの作成
PGQLを使用して、リレーショナル・データベース表からプロパティ・グラフを作成できます。CREATE PROPERTY GRAPH文では、頂点に変換される一連の頂点の表と、エッジに変換される一連のエッジの表を定義します。各表にはキー、ラベルおよび一連の列プロパティを指定できます。列タイプCHAR、NCHAR、VARCHAR、VARCHAR2、NVARCHAR2、NUMBER、LONG、FLOAT、DATE、TIMESTAMPおよびTIMESTAMP WITH TIMEZONEは、CREATE PROPERTY GRAPH列プロパティでサポートされています。
CREATE PROPERTY GRAPH文がコールされると、グラフのプロパティ・グラフ・スキーマが作成され、データがソース表からプロパティ・グラフ・スキーマ表にコピーされます。グラフは1回かぎりのコピーとして作成され、ソース・データとの同期は自動的には保持されません。
例5-3 PgqlCreateExample1.java
この例は、一連のリレーショナル表からプロパティ・グラフを作成する方法を示しています。この例では表Person、HobbyおよびHobbiesが作成されるため、この例を実行する前にはこれらの表が存在しないことに注意してください。この例は、プロパティ・グラフに対して問合せを実行する方法も示しています。
import java.sql.Connection;
import java.sql.Statement;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to create a Property Graph from relational
* data stored in Oracle Database executing a PGQL create statement.
*/
public class PgqlCreateExample1
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
Statement stmt = null;
PgqlStatement pgqlStmt = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
conn.setAutoCommit(false);
// Create relational data
stmt = conn.createStatement();
//Table Person
stmt.executeUpdate(
"create table Person( " +
" id NUMBER, " +
" name VARCHAR2(20), " +
" dob TIMESTAMP " +
")");
// Insert some data
stmt.executeUpdate("insert into Person values(1,'Alan', DATE '1995-05-26')");
stmt.executeUpdate("insert into Person values(2,'Ben', DATE '2007-02-15')");
stmt.executeUpdate("insert into Person values(3,'Claire', DATE '1967-11-30')");
// Table Hobby
stmt.executeUpdate(
"create table Hobby( " +
" id NUMBER, " +
" name VARCHAR2(20) " +
")");
// Insert some data
stmt.executeUpdate("insert into Hobby values(1, 'Sports')");
stmt.executeUpdate("insert into Hobby values(2, 'Music')");
// Table Hobbies
stmt.executeUpdate(
"create table Hobbies( "+
" person NUMBER, "+
" hobby NUMBER, "+
" strength NUMBER "+
")");
// Insert some data
stmt.executeUpdate("insert into Hobbies values(1, 1, 20)");
stmt.executeUpdate("insert into Hobbies values(1, 2, 30)");
stmt.executeUpdate("insert into Hobbies values(2, 1, 10)");
stmt.executeUpdate("insert into Hobbies values(3, 2, 20)");
//Commit changes
conn.commit();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
// Create a PgqlStatement
pgqlStmt = pgqlConn.createStatement();
// Execute PGQL to create property graph
String pgql =
"Create Property Graph " + graph + " " +
"VERTEX TABLES ( " +
" Person " +
" Key(id) " +
" Label \"people\" +
" PROPERTIES(name AS \"first_name\", dob AS \"birthday\")," +
" Hobby " +
" Key(id) Label \"hobby\" PROPERTIES(name AS \"name\")" +
")" +
"EDGE TABLES (" +
" Hobbies" +
" SOURCE KEY(person) REFERENCES Person " +
" DESTINATION KEY(hobby) REFERENCES Hobby " +
" LABEL \"likes\" PROPERTIES (strength AS \"score\")" +
")";
pgqlStmt.execute(pgql);
// Execute a PGQL query to verify Graph creation
pgql =
"SELECT p.\"first_name\", p.\"birthday\", h.\"name\", e.\"score\" " +
"FROM MATCH (p:\"people\")-[e:\"likes\"]->(h:\"hobby\") ON " + graph;
rs = pgqlStmt.executeQuery(pgql, "");
// Print the results
rs.print();
}
finally {
// close the sql statment
if (stmt != null) {
stmt.close();
}
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (pgqlStmt != null) {
pgqlStmt.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlCreateExample1.java
の出力は次のとおりです。
+---------------------------------------------------------+ | first_name | birthday | name | score | +---------------------------------------------------------+ | Alan | 1995-05-25 17:00:00.0 | Music | 30.0 | | Claire | 1967-11-29 16:00:00.0 | Music | 20.0 | | Ben | 2007-02-14 16:00:00.0 | Sports | 10.0 | | Alan | 1995-05-25 17:00:00.0 | Sports | 20.0 | +---------------------------------------------------------+
例5-4 PgqlCreateExample2.java
この例は、キーを指定せずにプロパティ・グラフ文を作成する方法を示しています。この例では表Person、HobbyおよびHobbiesが作成されるため、この例を実行する前にはこれらの表が存在しないことに注意してください。
import java.sql.Connection;
import java.sql.Statement;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to create a Property Graph from relational
* data stored in Oracle Database executing a PGQL create statement.
*/
public class PgqlCreateExample2
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
Statement stmt = null;
PgqlStatement pgqlStmt = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
conn.setAutoCommit(false);
// Create relational data
stmt = conn.createStatement();
//Table Person
stmt.executeUpdate(
"create table Person( " +
" id NUMBER, " +
" name VARCHAR2(20), " +
" dob TIMESTAMP, " +
" CONSTRAINT pk_person PRIMARY KEY(id)" +
")");
// Insert some data
stmt.executeUpdate("insert into Person values(1,'Alan', DATE '1995-05-26')");
stmt.executeUpdate("insert into Person values(2,'Ben', DATE '2007-02-15')");
stmt.executeUpdate("insert into Person values(3,'Claire', DATE '1967-11-30')");
// Table Hobby
stmt.executeUpdate(
"create table Hobby( " +
" id NUMBER, " +
" name VARCHAR2(20), " +
" CONSTRAINT pk_hobby PRIMARY KEY(id)" +
")");
// Insert some data
stmt.executeUpdate("insert into Hobby values(1, 'Sports')");
stmt.executeUpdate("insert into Hobby values(2, 'Music')");
// Table Hobbies
stmt.executeUpdate(
"create table Hobbies( "+
" person NUMBER, "+
" hobby NUMBER, "+
" strength NUMBER, "+
" CONSTRAINT fk_hobbies1 FOREIGN KEY (person) REFERENCES Person(id), "+
" CONSTRAINT fk_hobbies2 FOREIGN KEY (hobby) REFERENCES Hobby(id)"+
")");
// Insert some data
stmt.executeUpdate("insert into Hobbies values(1, 1, 20)");
stmt.executeUpdate("insert into Hobbies values(1, 2, 30)");
stmt.executeUpdate("insert into Hobbies values(2, 1, 10)");
stmt.executeUpdate("insert into Hobbies values(3, 2, 20)");
//Commit changes
conn.commit();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
// Create a PgqlStatement
pgqlStmt = pgqlConn.createStatement();
// Execute PGQL to create property graph
String pgql =
"Create Property Graph " + graph + " " +
"VERTEX TABLES ( " +
" Person " +
" Label people +
" PROPERTIES ALL COLUMNS," +
" Hobby " +
" Label hobby PROPERTIES ALL COLUMNS EXCEPT(id)" +
")" +
"EDGE TABLES (" +
" Hobbies" +
" SOURCE Person DESTINATION Hobby " +
" LABEL likes NO PROPERTIES" +
")";
pgqlStmt.execute(pgql);
// Execute a PGQL query to verify Graph creation
pgql =
"SELECT p.NAME AS person, p.DOB, h.NAME AS hobby " +
"FROM MATCH (p:people)-[e:likes]->(h:hobby) ON " + graph;
rs = pgqlStmt.executeQuery(pgql, "");
// Print the results
rs.print();
}
finally {
// close the sql statment
if (stmt != null) {
stmt.close();
}
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (pgqlStmt != null) {
pgqlStmt.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlCreateExample2.java
の出力は次のとおりです。
+-----------------------------------------+ | PERSON | DOB | HOBBY | +-----------------------------------------+ | Alan | 1995-05-25 17:00:00.0 | Music | | Claire | 1967-11-29 16:00:00.0 | Music | | Ben | 2007-02-14 16:00:00.0 | Sports | | Alan | 1995-05-25 17:00:00.0 | Sports | +-----------------------------------------+
5.9.3 DROP PROPERTY GRAPH文によるプロパティ・グラフの削除
PGQLを使用してプロパティ・グラフを削除できます。DROP PROPERTY GRAPH文がコールされると、グラフのすべてのプロパティ・グラフ・スキーマ表が削除されます。
例5-5 PgqlDropExample1.java
この例は、プロパティ・グラフを削除する方法を示しています。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to drop a Property executing a PGQL drop statement.
*/
public class PgqlDropExample1
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement pgqlStmt = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
conn.setAutoCommit(false);
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
// Create a PgqlStatement
pgqlStmt = pgqlConn.createStatement();
// Execute PGQL to drop property graph
String pgql = "Drop Property Graph " + graph;
pgqlStmt.execute(pgql);
}
finally {
// close the statement
if (pgqlStmt != null) {
pgqlStmt.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
5.9.4 oracle.pg.rdbms.pgql Javaパッケージを使用したPGQL問合せの実行
oracle.pg.rdbms.pgql
パッケージのJava APIは、Oracle Databaseに対してPGQL問合せを実行するためのサポートを提供します。このトピックでは、一連の例を使用してJava APIの使用方法について説明します。
ノート:
リリース21cでは、oracle.pg.rdbms
パッケージの次のクラスは非推奨になりました。
oracle.pg.rdbms.OraclePgqlColumnDescriptorImpl oracle.pg.rdbms.OraclePgqlColumnDescriptor oracle.pg.rdbms.OraclePgqlExecutionFactory oracle.pg.rdbms.OraclePgqlExecution oracle.pg.rdbms.PgqlPreparedStatement oracle.pg.rdbms.OraclePgqlResultElementImpl oracle.pg.rdbms.OraclePgqlResultElement oracle.pg.rdbms.OraclePgqlResultImpl oracle.pg.rdbms.OraclePgqlResultIterable oracle.pg.rdbms.OraclePgqlResultIteratorImpl oracle.pg.rdbms.OraclePgqlResult oracle.pg.rdbms.OraclePgqlResultSetImpl oracle.pg.rdbms.OraclePgqlResultSet oracle.pg.rdbms.OraclePgqlResultSetMetaDataImpl oracle.pg.rdbms.OraclePgqlResultSetMetaData oracle.pg.rdbms.PgqlSqlQueryTransImpl oracle.pg.rdbms.PgqlSqlQueryTrans oracle.pg.rdbms.PgqlStatement
かわりに、oracle.pg.rdbms.pgql
内の同等のクラスを使用する必要があります。
oracle.pg.rdbms.pgql.PgqlColumnDescriptorImpl oracle.pg.rdbms.pgql.PgqlColumnDescriptor oracle.pg.rdbms.pgql.PgqlConnection oracle.pg.rdbms.pgql.PgqlExecution oracle.pg.rdbms.pgql.PgqlPreparedStatement oracle.pg.rdbms.pgql.PgqlResultElementImpl oracle.pg.rdbms.pgql.PgqlResultElement oracle.pg.rdbms.pgql.PgqlResultSetImpl oracle.pg.rdbms.pgql.PgqlResultSet oracle.pg.rdbms.pgql.PgqlResultSetMetaDataImpl oracle.pg.rdbms.pgql.PgqlSqlTransImpl oracle.pg.rdbms.pgql.PgqlSqlTrans oracle.pg.rdbms.pgql.PgqlStatement
oracle.pg.rdbms.OraclePgqlResultSet
とoracle.pg.rdbms.pgql.PgqlResultSet
の違いの1つは、oracle.pg.rdbms.pgql.PgqlResultSetが、頂点オブジェクトおよびエッジ・オブジェクトを取得するためのAPIを提供しないことです。これらのインタフェースを使用している既存のコードは、OracleVertex
およびOracleEdge
オブジェクトではなくIDを射影するように変更する必要があります。OracleVertex.getInstance()
またはOracleEdge.getInstance()
を呼び出すことで、射影されたID値からOracleVertex
またはOracleEdge
オブジェクトを取得できます。(たとえば、例5-20を参照してください。)
次に示す、Oracleフラット・ファイル形式のtest_graph
データ・セットは、この後のサブトピック内の例で使用されます。このデータ・セットには、1つの頂点ファイル(test_graph.opv
)と1つのエッジ・ファイル(test_graph.ope
).が含まれています
test_graph.opv
:
2,fname,1,Ray,,,person 2,lname,1,Green,,,person 2,mval,5,,,1985-01-01T12:00:00.000Z,person 2,age,2,,41,,person 0,bval,6,Y,,,person 0,fname,1,Bill,,,person 0,lname,1,Brown,,,person 0,mval,1,y,,,person 0,age,2,,40,,person 1,bval,6,Y,,,person 1,fname,1,John,,,person 1,lname,1,Black,,,person 1,mval,2,,27,,person 1,age,2,,30,,person 3,bval,6,N,,,person 3,fname,1,Susan,,,person 3,lname,1,Blue,,,person 3,mval,6,N,,,person 3,age,2,,35,,person
test_graph.ope
:
4,0,1,knows,mval,1,Y,, 4,0,1,knows,firstMetIn,1,MI,, 4,0,1,knows,since,5,,,1990-01-01T12:00:00.000Z 16,0,1,friendOf,strength,2,,6, 7,1,0,knows,mval,5,,,2003-01-01T12:00:00.000Z 7,1,0,knows,firstMetIn,1,GA,, 7,1,0,knows,since,5,,,2000-01-01T12:00:00.000Z 17,1,0,friendOf,strength,2,,7, 9,1,3,knows,mval,6,N,, 9,1,3,knows,firstMetIn,1,SC,, 9,1,3,knows,since,5,,,2005-01-01T12:00:00.000Z 10,2,0,knows,mval,1,N,, 10,2,0,knows,firstMetIn,1,TX,, 10,2,0,knows,since,5,,,1997-01-01T12:00:00.000Z 12,2,3,knows,mval,3,,342.5, 12,2,3,knows,firstMetIn,1,TX,, 12,2,3,knows,since,5,,,2011-01-01T12:00:00.000Z 19,2,3,friendOf,strength,2,,4, 14,3,1,knows,mval,1,a,, 14,3,1,knows,firstMetIn,1,CA,, 14,3,1,knows,since,5,,,2010-01-01T12:00:00.000Z 15,3,2,knows,mval,1,z,, 15,3,2,knows,firstMetIn,1,CA,, 15,3,2,knows,since,5,,,2004-01-01T12:00:00.000Z 5,0,2,knows,mval,2,,23, 5,0,2,knows,firstMetIn,1,OH,, 5,0,2,knows,since,5,,,2002-01-01T12:00:00.000Z 6,0,3,knows,mval,3,,159.7, 6,0,3,knows,firstMetIn,1,IN,, 6,0,3,knows,since,5,,,1994-01-01T12:00:00.000Z 8,1,2,knows,mval,6,Y,, 8,1,2,knows,firstMetIn,1,FL,, 8,1,2,knows,since,5,,,1999-01-01T12:00:00.000Z 18,1,3,friendOf,strength,2,,5, 11,2,1,knows,mval,2,,1001, 11,2,1,knows,firstMetIn,1,OK,, 11,2,1,knows,since,5,,,2003-01-01T12:00:00.000Z 13,3,0,knows,mval,5,,,2001-01-01T12:00:00.000Z 13,3,0,knows,firstMetIn,1,CA,, 13,3,0,knows,since,5,,,2006-01-01T12:00:00.000Z 20,3,1,friendOf,strength,2,,3,
5.9.4.1 基本的な問合せの実行
2つの主要なJavaインタフェース、PgqlStatement
とPgqlResultSet
がPGQLの実行に使用されます。このトピックには、基本的な問合せ実行のいくつかの例が含まれています。
例5-6 GraphLoaderExample.java
GraphLoaderExample.java
は、このトピックの後続の例で使用されるOracleプロパティ・グラフ・データをロードします。
import oracle.pg.rdbms.Oracle;
import oracle.pg.rdbms.OraclePropertyGraph;
import oracle.pg.rdbms.OraclePropertyGraphDataLoader;
/**
* This example shows how to create an Oracle Property Graph
* and load data into it from vertex and edge flat files.
*/
public class GraphLoaderExample
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
String vertexFile = args[idx++];
String edgeFile = args[idx++];
Oracle oracle = null;
OraclePropertyGraph opg = null;
try {
// Create a connection to Oracle
oracle = new Oracle("jdbc:oracle:thin:@"+host+":"+port +":"+sid, user, password);
// Create a property graph
opg = OraclePropertyGraph.getInstance(oracle, graph);
// Clear any existing data
opg.clearRepository();
// Load data from opv and ope files
OraclePropertyGraphDataLoader opgLoader = OraclePropertyGraphDataLoader.getInstance();
opgLoader.loadData(opg, vertexFile, edgeFile, 1);
System.out.println("Vertices loaded:" + opg.countVertices());
System.out.println("Edges loaded:" + opg.countEdges());
}
finally {
// close the property graph
if (opg != null) {
opg.close();
}
// close oracle
if (oracle != null) {
oracle.dispose();
}
}
}
}
GraphLoaderExample.java
は、次に示す test_graph
の出力を生成します。
Vertices loaded:4 Edges loaded:17
例5-7 PgqlExample1.java
PgqlExample1.java
は、PGQL問合せを実行し、問合せ結果を出力します。PgqlConnection
は、PgqlStatement
を取得するために使用されます。次に、PgqlStatement
のexecuteQuery
メソッドをコールし、これにより、PgqlResultSet
オブジェクトが返されます。PgqlResultSet
には、表形式モードで結果を表示するprint()
メソッドがあります。
PgqlResultSet
オブジェクトとPgqlStatement
オブジェクトは、問合せ結果の消費後にクローズする必要があります。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to execute a basic PGQL query against disk-resident
* PG data stored in Oracle Database and iterate through the result.
*/
public class PgqlExample1
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute query to get a PgqlResultSet object
String pgql =
"SELECT v.\"fname\" AS fname, v.\"lname\" AS lname, v.\"mval\" AS mval "+
"FROM MATCH (v)";
rs = ps.executeQuery(pgql, /* query string */
"" /* options */);
// Print the results
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample1.java
は、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対して次の出力を提供します。
+---------------------------------------+ | FNAME | LNAME | MVAL | +---------------------------------------+ | Susan | Blue | false | | Bill | Brown | y | | Ray | Green | 1985-01-01 04:00:00.0 | | John | Black | 27 | +---------------------------------------+
例5-8 PgqlExample2.java
PgqlExample2.java
は、エッジ・プロパティに対するTemporalフィルタを持つPGQL問合せを示しています。
PgqlResultSet
は、java.sql.ResultSet
とよく似ている問合せ結果を消費するためのインタフェースを提供します。next()
メソッドを使用すると、問合せ結果内で移動できます。また、close()
メソッドを使用すると、アプリケーションで問合せ結果の読込みが終了した後で、リソースを解放できます。- さらに、
PgqlResultSet
はString
、Integer
、Long
、Float
、Double
、Boolean
、LocalDateTime
、OffsetDateTime
に対するgetterを提供し、任意の型の値に対する汎用のgetObject()
メソッドを提供します。
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.Date;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.pgql.lang.ResultSet;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to execute a PGQL query with a temporal edge
* property filter against disk-resident PG data stored in Oracle Database
* and iterate through the result.
*/
public class PgqlExample2
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
ResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Create a Pgql connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute query to get a ResultSet object
String pgql =
"SELECT v.\"fname\" AS n1, v2.\"fname\" AS n2, e.\"firstMetIn\" AS loc "+
"FROM MATCH (v)-[e:\"knows\"]->(v2) "+
"WHERE e.\"since\" > TIMESTAMP '2000-01-01 00:00:00.00+00:00'";
rs = ps.executeQuery(pgql, "");
// Print results
printResults(rs);
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
/**
* Prints a PGQL ResultSet
*/
static void printResults(ResultSet rs) throws Exception
{
StringBuffer buff = new StringBuffer("");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
while (rs.next()) {
buff.append("[");
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
// use generic getObject to handle all types
Object mval = rs.getObject(i);
String mStr = "";
if (mval instanceof java.lang.String) {
mStr = "STRING: "+mval.toString();
}
else if (mval instanceof java.lang.Integer) {
mStr = "INTEGER: "+mval.toString();
}
else if (mval instanceof java.lang.Long) {
mStr = "LONG: "+mval.toString();
}
else if (mval instanceof java.lang.Float) {
mStr = "FLOAT: "+mval.toString();
}
else if (mval instanceof java.lang.Double) {
mStr = "DOUBLE: "+mval.toString();
}
else if (mval instanceof java.sql.Timestamp) {
mStr = "DATE: "+sdf.format((Date)mval);
}
else if (mval instanceof java.lang.Boolean) {
mStr = "BOOLEAN: "+mval.toString();
}
if (i > 1) {
buff.append(",\t");
}
buff.append(mStr);
}
buff.append("]\n");
}
System.out.println(buff.toString());
}
}
PgqlExample2.java
は、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対して次の出力を提供します。
[STRING: Susan, STRING: Bill, STRING: CA] [STRING: Susan, STRING: John, STRING: CA] [STRING: Susan, STRING: Ray, STRING: CA] [STRING: Bill, STRING: Ray, STRING: OH] [STRING: Ray, STRING: John, STRING: OK] [STRING: Ray, STRING: Susan, STRING: TX] [STRING: John, STRING: Susan, STRING: SC] [STRING: John, STRING: Bill, STRING: GA]
例5-9 PgqlExample3.java
PgqlExample3.java
は、グループ化と集計を含むPGQL問合せを示しています。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to execute a PGQL query with aggregation
* against disk-resident PG data stored in Oracle Database and iterate
* through the result.
*/
public class PgqlExample3
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Create a Pgql connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute query to get a ResultSet object
String pgql =
"SELECT v.\"fname\" AS \"fname\", COUNT(v2) AS \"friendCnt\" "+
"FROM MATCH (v)-[e:\"friendOf\"]->(v2) "+
"GROUP BY v "+
"ORDER BY \"friendCnt\" DESC";
rs = ps.executeQuery(pgql, "");
// Print results
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample3.java
は、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対して次の出力を提供します。
+-------------------+ | fname | friendCnt | +-------------------+ | John | 2 | | Bill | 1 | | Ray | 1 | | Susan | 1 | +-------------------+
例5-10 PgqlExample4.java
PgqlExample4.java
は、PGQLパス問合せを示しています。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to execute a path query in PGQL against
* disk-resident PG data stored in Oracle Database and iterate
* through the result.
*/
public class PgqlExample4
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Create a Pgql connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute query to get a ResultSet object
String pgql =
"PATH fof AS ()-[:\"friendOf\"|\"knows\"]->() "+
"SELECT v2.\"fname\" AS friend "+
"FROM MATCH (v)-/:fof*/->(v2) "+
"WHERE v.\"fname\" = 'John' AND v != v2";
rs = ps.executeQuery(pgql, "");
// Print results
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample4.java
は、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対して次の出力を提供します。
+--------+ | FRIEND | +--------+ | Susan | | Bill | | Ray | +--------+
5.9.4.2 PGQL問合せのセキュリティ手法
動的な問合せを実行するプログラムは、アプリケーションの整合性と機能を損なう可能性のあるインジェクション攻撃の影響を受ける可能性があります。
このトピックでは、文字列連結を使用してPGQL問合せを作成する際に、インジェクション攻撃を防ぐために使用できるいくつかの手法について説明します。
5.9.4.2.1 PGQL問合せでのバインド変数の使用
パフォーマンス改善とセキュリティ向上のためにPGQL問合せでバインド変数を使用できます。PGQL問合せの定数スカラー値は、バインド変数に置き換えることができます。バインド変数は、'?' (疑問符)で示されます。一定の年齢値よりも上の年齢の人を選択する、次の2つの問合せについて検討します。
// people older than 30
SELECT v.fname AS fname, v.lname AS lname, v.age AS age
FROM MATCH (v)
WHERE v.age > 30
// people older than 40
SELECT v.fname AS fname, v.lname AS lname, v.age AS age
FROM MATCH (v)
WHERE v.age > 40
これらの問合せのSQL変換は、年齢フィルタに同様の方法で定数30および40を使用します。データベースは、これらの各問合せのハード解析を実行します。このハード解析時間は、単純な問合せの実行時間を超過することがよくあります。
次のように、各問合せ内の定数をバインド変数に置き換えることができます。
SELECT v.fname AS fname, v.lname AS lname, v.age AS age
FROM MATCH (v)
WHERE v.age > ?
これにより、SQLエンジンは、様々な年齢値に再利用できる、この問合せの汎用カーソルを作成できます。その結果、ハード解析が様々な年齢値に対してこの問合せを実行する必要がなくなり、各問合せの解析時間が大幅に短縮されます。
さらに、PGQL問合せでバインド変数を使用するアプリケーションでは、文字列連結を使用して定数値をPGQL問合せに埋め込むものに比べ、インジェクション攻撃に対する脆弱性が軽減されます。
カーソル共有とバインド変数の詳細は、『Oracle Database SQLチューニング・ガイド』も参照してください。
PgqlExample5.java
に示すように、PgqlPreparedStatement
インタフェースは、バインド変数を使用する問合せを実行するために使用できます。PgqlPreparedStatement
には、問合せ実行のための値の設定に使用できる様々な値タイプ用の複数の設定メソッドが用意されています。
PGQLではバインド変数にいくつかの制限があります。バインド変数は、定数プロパティ値にのみを使用できます。つまり、頂点とエッジをバインド変数に置き換えることはできません。また、一旦特定のバインド変数をある型に設定したら、それを別の型に設定することはできません。たとえば、PgqlPreparedStatement
に対してsetInt(1, 30)
が実行される場合、その同じPgqlPreparedStatement
でsetString(1, "abc")
をコールすることはできません。
例5-11 PgqlExample5.java
PgqlExample5.java
は、PGQL問合せでのバインド変数の使用方法を示しています。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlPreparedStatement;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to use bind variables with a PGQL query.
*/
public class PgqlExample5
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlPreparedStatement pps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Create a Pgql connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Query string with a bind variable (denoted by ?)
String pgql =
"SELECT v.\"fname\" AS fname, v.\"lname\" AS lname, v.\"age\" AS age "+
"FROM MATCH (v) "+
"WHERE v.\"age\" > ?";
// Create a PgqlPreparedStatement
pps = pgqlConn.prepareStatement(pgql);
// Set filter value to 30
pps.setInt(1, 30);
// execute query
rs = pps.executeQuery();
// Print query results
System.out.println("-- Values for v.\"age\" > 30 --");
rs.print();
// close result set
rs.close();
// set filter value to 40
pps.setInt(1, 40);
// execute query
rs = pps.executeQuery();
// Print query results
System.out.println("-- Values for v.\"age\" > 40 --");
rs.print();
// close result set
rs.close();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (pps != null) {
pps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample5.java
には、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対する次の出力があります。
-- Values for v.age > 30 -- +---------------------+ | fname | lname | age | +---------------------+ | Susan | Blue | 35 | | Bill | Brown | 40 | | Ray | Green | 41 | +---------------------+ -- Values for v.age > 40 -- +---------------------+ | fname | lname | age | +---------------------+ | Ray | Green | 41 | +---------------------+
例5-12 PgqlExample6.java
PgqlExample6.java
は、2つのバインド変数、1つのString変数と1つのTimestamp変数を含む問合せを示しています。
import java.sql.Connection;
import java.sql.Timestamp;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlPreparedStatement;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to use multiple bind variables with a PGQL query.
*/
public class PgqlExample6
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlPreparedStatement pps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Create a Pgql connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Query string with multiple bind variables
String pgql =
"SELECT v1.\"fname\" AS fname1, v2.\"fname\" AS fname2 "+
"FROM MATCH (v1)-[e:\"knows\"]->(v2) "+
"WHERE e.\"since\" < ? AND e.\"firstMetIn\" = ?";
// Create a PgqlPreparedStatement
pps = pgqlConn.prepareStatement(pgql);
// Set e.since < 2006-01-01T12:00:00.00Z
Timestamp t = Timestamp.valueOf(OffsetDateTime.parse("2006-01-01T12:00:01.00Z").atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime());
pps.setTimestamp(1, t);
// Set e.firstMetIn = 'CA'
pps.setString(2, "CA");
// execute query
rs = pps.executeQuery();
// Print query results
System.out.println("-- Values for e.\"since\" < 2006-01-01T12:00:01.00Z AND e.\"firstMetIn\" = 'CA' --");
rs.print();
// close result set
rs.close();
// Set e.since < 2000-01-01T12:00:00.00Z
t = Timestamp.valueOf(OffsetDateTime.parse("2000-01-01T12:00:00.00Z").atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime());
pps.setTimestamp(1, t);
// Set e.firstMetIn = 'TX'
pps.setString(2, "TX");
// execute query
rs = pps.executeQuery();
// Print query results
System.out.println("-- Values for e.\"since\" < 2000-01-01T12:00:00.00Z AND e.\"firstMetIn\" = 'TX' --");
rs.print();
// close result set
rs.close();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (pps != null) {
pps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample6.java
は、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対して次の出力を提供します。
-- Values for e."since" < 2006-01-01T12:00:01.00Z AND e."firstMetIn" = 'CA' -- +-----------------+ | FNAME1 | FNAME2 | +-----------------+ | Susan | Bill | | Susan | Ray | +-----------------+ -- Values for e."since" < 2000-01-01T12:00:00.00Z AND e."firstMetIn" = 'TX' -- +-----------------+ | FNAME1 | FNAME2 | +-----------------+ | Ray | Bill | +-----------------+
親トピック: PGQL問合せのセキュリティ手法
5.9.4.2.2 PGQL識別子の検証
PGQL問合せの一部では、パーサーはバインド変数を使用できません。そのような場合は、パッケージoracle.pgql.lang.ir.PgqlUtils
のprintIdentifier
メソッドを使用して入力を検証できます。
グラフ・パターンが照合されるグラフを連結する、次の問合せ実行について考えてみます。
stmt.executeQuery("SELECT n.name FROM MATCH (n) ON " + graphName, "");
インジェクションを回避するために、識別子graphName
を次に示すように検証する必要があります。
stmt.executeQuery("SELECT n.name FROM MATCH (n) ON " + PgqlUtils.printIdentifier(graphName), "");
親トピック: PGQL問合せのセキュリティ手法
5.9.4.3 PGQL問合せでのテキスト索引の使用
Oracle Databaseに対して実行されたPGQL問合せは、頂点プロパティとエッジ・プロパティ用に作成されたOracle Text索引を使用できます。テキスト索引を作成した後は、CONTAINS演算子を使用して全文検索を実行できます。CONTAINSには、頂点プロパティまたはエッジ・プロパティとOracle Text検索文字列という2つの引数があります。ワイルドカード、Stemming、およびSoundexなどの高度な機能を含め、すべての有効なOracle Text検索文字列を使用できます。
例5-13 PgqlExample7.java
PgqlExample7.java
は、CONTAINS問合せの実行方法を示しています。
import java.sql.CallableStatement;
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to use an Oracle Text index with a PGQL query.
*/
public class PgqlExample7
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Create text index with SQL API
CallableStatement cs = null;
// text index on vertices
cs = conn.prepareCall(
"begin opg_apis.create_vertices_text_idx(:1,:2); end;"
);
cs.setString(1,user);
cs.setString(2,graph);
cs.execute();
cs.close();
// text index on edges
cs = conn.prepareCall(
"begin opg_apis.create_edges_text_idx(:1,:2); end;"
);
cs.setString(1,user);
cs.setString(2,graph);
cs.execute();
cs.close();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Query using CONTAINS text search operator on vertex property
// Find all vertices with an lname property value that starts with 'B'
String pgql =
"SELECT v.\"fname\" AS fname, v.\"lname\" AS lname "+
"FROM MATCH (v) "+
"WHERE CONTAINS(v.\"lname\",'B%')";
// execute query
rs = ps.executeQuery(pgql, "");
// print results
System.out.println("-- Vertex Property Query --");
rs.print();
// close result set
rs.close();
// Query using CONTAINS text search operator on edge property
// Find all knows edges with a firstMetIn property value that ends with 'A'
pgql =
"SELECT v1.\"fname\" AS fname1, v2.\"fname\" AS fname2, e.\"firstMetIn\" AS loc "+
"FROM MATCH (v1)-[e:\"knows\"]->(v2) "+
"WHERE CONTAINS(e.\"firstMetIn\",'%A')";
// execute query
rs = ps.executeQuery(pgql, "");
// print results
System.out.println("-- Edge Property Query --");
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample7.java
には、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対する次の出力があります。
-- Vertex Property Query -- +---------------+ | FNAME | LNAME | +---------------+ | Susan | Blue | | Bill | Brown | | John | Black | +---------------+ -- Edge Property Query -- +-----------------------+ | FNAME1 | FNAME1 | LOC | +-----------------------+ | Susan | Bill | CA | | John | Bill | GA | | Susan | John | CA | | Susan | Ray | CA | +-----------------------+
5.9.4.4 PGQL問合せのSQL変換の取得
PgqlStatement
メソッドとPgqlPreparedStatement
メソッドを介してPGQL問合せのSQL変換を取得できます。PGQL問合せの未加工のSQLは、次のいくつかの理由で役立ちます。
-
他のSQLベースのツールまたはインタフェース(たとえば、SQL*PlusやSQL Developer)を使用してデータベースに対してSQLを直接実行できます。
-
生成されたSQLをカスタマイズおよびチューニングして、パフォーマンスの最適化やアプリケーションの特定要件の達成を実行できます。
-
PGQL副問合せを、Oracle Databaseに格納されている他のデータ(リレーショナル表、空間データ、およびJSONデータなど)と結合する大規模なSQL問合せを作成できます。
例5-14 PgqlExample8.java
PgqlExample8.java
は、PGQL問合せの未加工のSQL変換の取得方法を示しています。PgqlStatement
のtranslateQuery
メソッドは、問合せからの戻り列とSQL変換自体に関する情報を含むPgqlSqlQueryTrans
オブジェクトを返します。
変換されたSQLは、PGQL問合せから射影される「論理」オブジェクトまたは値の型に応じて異なる列を返します。PGQLに射影される頂点またはエッジには、変換されたSQLに射影される、2つの対応する列があります。
-
$IT : ID型– NVARCHAR(1): 「V」は頂点、「E」はエッジ。
-
$ID :頂点IDまたはエッジID – NUMBER: VT$表とGE$表内のVID列またはEID例と同じ内容
PGQLに射影されているプロパティ値または定数スカラー値には、変換されたSQLに射影される4つの対応する列があります。
-
$T :値の型– NUMBER: VT$表とGE$表内のT列と同じ内容
-
$V:値– NVARCHAR2(15000): VT$表とGE$表内のV列と同じ内容
-
$VN:数値の値– NUMBER: VT$表とGE$表内のVN列と同じ内容
-
$VT: Temporal値– TIMESTAMP WITH TIME ZONE: VT$表とGE$表内のVT列と同じ内容
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlColumnDescriptor;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.pg.rdbms.pgql.PgqlSqlQueryTrans;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to obtain the SQL translation for a PGQL query.
*/
public class PgqlExample8
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Create a Pgql connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// PGQL query to be translated
String pgql =
"SELECT v1, v1.\"fname\" AS fname1, e, e.\"since\" AS since "+
"FROM MATCH (v1)-[e:\"knows\"]->(v2)";
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Get the SQL translation
PgqlSqlQueryTrans sqlTrans = ps.translateQuery(pgql,"");
// Get the return column descriptions
PgqlColumnDescriptor[] cols = sqlTrans.getReturnTypes();
// Print column descriptions
System.out.println("-- Return Columns -----------------------");
printReturnCols(cols);
// Print SQL translation
System.out.println("-- SQL Translation ----------------------");
System.out.println(sqlTrans.getSqlTranslation());
}
finally {
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
/**
* Prints return columns for a SQL translation
*/
static void printReturnCols(PgqlColumnDescriptor[] cols) throws Exception
{
StringBuffer buff = new StringBuffer("");
for (int i = 0; i < cols.length; i++) {
String colName = cols[i].getColName();
PgqlColumnDescriptor.Type colType = cols[i].getColType();
int offset = cols[i].getSqlOffset();
String readableType = "";
switch(colType) {
case VERTEX:
readableType = "VERTEX";
break;
case EDGE:
readableType = "EDGE";
break;
case VALUE:
readableType = "VALUE";
break;
}
buff.append("colName=["+colName+"] colType=["+readableType+"] offset=["+offset+"]\n");
}
System.out.println(buff.toString());
}
}
PgqlExample8.java
には、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対する次の出力があります。
-- Return Columns ----------------------- colName=[v1] colType=[VERTEX] offset=[1] colName=[fname1] colType=[VALUE] offset=[3] colName=[e] colType=[EDGE] offset=[7] colName=[since] colType=[VALUE] offset=[9] -- SQL Translation ---------------------- SELECT n'V' AS "V1$IT", T0$0.SVID AS "V1$ID", T0$1.T AS "FNAME1$T", T0$1.V AS "FNAME1$V", T0$1.VN AS "FNAME1$VN", T0$1.VT AS "FNAME1$VT", n'E' AS "E$IT", T0$0.EID AS "E$ID", T0$0.T AS "SINCE$T", T0$0.V AS "SINCE$V", T0$0.VN AS "SINCE$VN", T0$0.VT AS "SINCE$VT" FROM ( SELECT L.EID, L.SVID, L.DVID, L.EL, R.K, R.T, R.V, R.VN, R.VT FROM "SCOTT".TEST_GRAPHGT$ L, (SELECT * FROM "SCOTT".TEST_GRAPHGE$ WHERE K=n'since' ) R WHERE L.EID = R.EID(+) ) T0$0, ( SELECT L.VID, L.VL, R.K, R.T, R.V, R.VN, R.VT FROM "SCOTT".TEST_GRAPHVD$ L, (SELECT * FROM "SCOTT".TEST_GRAPHVT$ WHERE K=n'fname' ) R WHERE L.VID = R.VID(+) ) T0$1 WHERE T0$0.SVID=T0$1.VID AND (T0$0.EL = n'knows' AND T0$0.EL IS NOT NULL)
例5-15 PgqlExample9.java
また、バインド変数を含むPGQL問合せのSQL変換も取得できます。この場合、対応するSQL変換にもバインド変数が含まれます。PgqlSqlQueryTrans
インタフェースには、SQL問合せにバインドされている必要があるJavaオブジェクトの順序付きリスト(リストの最初のオブジェクトは1の位置に設定され、2番目は2の位置に設定されている必要がある)を返すgetSqlBvList
メソッドがあります。
PgqlExample9.java
は、バインド変数を使用したPGQL問合せのSQLの取得および実行方法を示しています。
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.List;
import oracle.pg.rdbms.pgql.PgqlColumnDescriptor;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlPreparedStatement;
import oracle.pg.rdbms.pgql.PgqlSqlQueryTrans;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to obtain and execute the SQL translation for a
* PGQL query that uses bind variables.
*/
public class PgqlExample9
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlPreparedStatement pgqlPs = null;
PreparedStatement sqlPs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Create a Pgql connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Execute query to get a ResultSet object
String pgql =
"SELECT v1, v1.\"fname\" AS fname1, v1.\"age\" AS age, ? as constVal "+
"FROM MATCH (v1) "+
"WHERE v1.\"fname\" = ? OR v1.\"age\" < ?";
// Create a PgqlStatement
pgqlPs = pgqlConn.prepareStatement(pgql);
// set bind values
pgqlPs.setDouble(1, 2.05d);
pgqlPs.setString(2, "Bill");
pgqlPs.setInt(3, 35);
// Get the SQL translation
PgqlSqlQueryTrans sqlTrans = pgqlPs.translateQuery("");
// Get the SQL String
String sqlStr = sqlTrans.getSqlTranslation();
// Get the return column descriptions
PgqlColumnDescriptor[] cols = sqlTrans.getReturnTypes();
// Get the bind values
List<Object> bindVals = sqlTrans.getSqlBvList();
// Print column descriptions
System.out.println("-- Return Columns -----------------------");
printReturnCols(cols);
// Print SQL translation
System.out.println("-- SQL Translation ----------------------");
System.out.println(sqlStr);
// Print Bind Values
System.out.println("\n-- Bind Values --------------------------");
for (Object obj : bindVals) {
System.out.println(obj.toString());
}
// Execute Query
// Get PreparedStatement
sqlPs = conn.prepareStatement("SELECT COUNT(*) FROM ("+sqlStr+")");
// Set bind values and execute the PreparedStatement
executePs(sqlPs, bindVals);
// Set new bind values in the PGQL PreparedStatement
pgqlPs.setDouble(1, 3.02d);
pgqlPs.setString(2, "Ray");
pgqlPs.setInt(3, 30);
// Print Bind Values
bindVals = sqlTrans.getSqlBvList();
System.out.println("\n-- Bind Values --------------------------");
for (Object obj : bindVals) {
System.out.println(obj.toString());
}
// Execute the PreparedStatement with new bind values
executePs(sqlPs, bindVals);
}
finally {
// close the SQL statement
if (sqlPs != null) {
sqlPs.close();
}
// close the statement
if (pgqlPs != null) {
pgqlPs.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
/**
* Executes a SQL PreparedStatement with the input bind values
*/
static void executePs(PreparedStatement ps, List<Object> bindVals) throws Exception
{
ResultSet rs = null;
try {
// Set bind values
for (int idx = 0; idx < bindVals.size(); idx++) {
Object o = bindVals.get(idx);
// String
if (o instanceof java.lang.String) {
ps.setNString(idx + 1, (String)o);
}
// Int
else if (o instanceof java.lang.Integer) {
ps.setInt(idx + 1, ((Integer)o).intValue());
}
// Long
else if (o instanceof java.lang.Long) {
ps.setLong(idx + 1, ((Long)o).longValue());
}
// Float
else if (o instanceof java.lang.Float) {
ps.setFloat(idx + 1, ((Float)o).floatValue());
}
// Double
else if (o instanceof java.lang.Double) {
ps.setDouble(idx + 1, ((Double)o).doubleValue());
}
// Timestamp
else if (o instanceof java.sql.Timestamp) {
ps.setTimestamp(idx + 1, (Timestamp)o);
}
else {
ps.setString(idx + 1, bindVals.get(idx).toString());
}
}
// Execute query
rs = ps.executeQuery();
if (rs.next()) {
System.out.println("\n-- Execute Query: Result has "+rs.getInt(1)+" rows --");
}
}
finally {
// close the SQL ResultSet
if (rs != null) {
rs.close();
}
}
}
/**
* Prints return columns for a SQL translation
*/
static void printReturnCols(PgqlColumnDescriptor[] cols) throws Exception
{
StringBuffer buff = new StringBuffer("");
for (int i = 0; i < cols.length; i++) {
String colName = cols[i].getColName();
PgqlColumnDescriptor.Type colType = cols[i].getColType();
int offset = cols[i].getSqlOffset();
String readableType = "";
switch(colType) {
case VERTEX:
readableType = "VERTEX";
break;
case EDGE:
readableType = "EDGE";
break;
case VALUE:
readableType = "VALUE";
break;
}
buff.append("colName=["+colName+"] colType=["+readableType+"] offset=["+offset+"]\n");
}
System.out.println(buff.toString());
}
}
PgqlExample9.java
には、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対する次の出力があります。
–-- Return Columns ----------------------- colName=[v1] colType=[VERTEX] offset=[1] colName=[fname1] colType=[VALUE] offset=[3] colName=[age] colType=[VALUE] offset=[7] colName=[constVal] colType=[VALUE] offset=[11] -- SQL Translation ---------------------- SELECT n'V' AS "V1$IT", T0$0.VID AS "V1$ID", T0$0.T AS "FNAME1$T", T0$0.V AS "FNAME1$V", T0$0.VN AS "FNAME1$VN", T0$0.VT AS "FNAME1$VT", T0$1.T AS "AGE$T", T0$1.V AS "AGE$V", T0$1.VN AS "AGE$VN", T0$1.VT AS "AGE$VT", 4 AS "CONSTVAL$T", to_nchar(?,'TM9','NLS_Numeric_Characters=''.,''') AS "CONSTVAL$V", ? AS "CONSTVAL$VN", to_timestamp_tz(null) AS "CONSTVAL$VT" FROM ( SELECT L.VID, L.VL, R.K, R.T, R.V, R.VN, R.VT FROM "SCOTT".TEST_GRAPHVD$ L, (SELECT * FROM "SCOTT".TEST_GRAPHVT$ WHERE K=n'fname' ) R WHERE L.VID = R.VID(+) ) T0$0, ( SELECT L.VID, L.VL, R.K, R.T, R.V, R.VN, R.VT FROM "SCOTT".TEST_GRAPHVD$ L, (SELECT * FROM "SCOTT".TEST_GRAPHVT$ WHERE K=n'age' ) R WHERE L.VID = R.VID(+) ) T0$1 WHERE T0$0.VID=T0$1.VID AND ((T0$0.T = 1 AND T0$0.V = ?) OR T0$1.VN < ?) -- Bind Values -------------------------- 2.05 2.05 Bill 35 -- Execute Query: Result has 2 rows -- -- Bind Values -------------------------- 3.02 3.02 Ray 30 -- Execute Query: Result has 1 rows --
5.9.4.5 PGQL変換および実行の追加オプション
PGQL問合せの変換および実行に影響を与える複数のオプションがあります。問合せオプションを設定する主な方法は次のとおりです。
-
executeQuery
およびtranslateQuery
に対して明示的引数を使用する -
executeQuery
およびtranslateQuery
のoptions
文字列引数でフラグを使用する -
Java JVM引数を使用する
次の表に、PGQLの変換と実行に使用できる問合せ引数をまとめています。
表5-2 PGQLの変換および実行のオプション
オプション | デフォルト | 明示的引数 | オプション・フラグ | JVM引数 |
---|---|---|---|---|
並列度 |
0 |
parallel |
なし |
なし |
タイムアウト |
unlimited |
timeout |
なし |
なし |
動的サンプリング |
2 |
dynamicSampling |
なし |
なし |
結果の最大数 |
unlimited |
maxResults |
なし |
なし |
GT$表の使用 |
on |
なし |
USE_GT_TAB=F |
-Doracle.pg.rdbms.pgql.useGtTab=false |
CONNECT BYの使用 |
off |
なし |
USE_RW=F |
-Doracle.pg.rdbms.pgql.useRW=false |
明確な再帰的WITHの使用 |
off |
なし |
USE_DIST_RW=T |
-Doracle.pg.rdbms.pgql.useDistRW=true |
パスの最大長 |
unlimited |
なし |
MAX_PATH_LEN=n |
-Doracle.pg.rdbms.pgql.maxPathLen=n |
partialの設定 |
false |
なし |
EDGE_SET_PARTIAL=T |
-Doracle.pg.rdbms.pgql.edgeSetPartial=true |
プロジェクトのNullプロパティ |
true |
なし |
PROJ_NULL_PROPS=F |
-Doracle.pg.rdbms.pgql.projNullProps=false |
VT$ VL列使用 |
on |
なし |
USE_VL_COL=F |
-Doracle.pg.rdbms.pgql.useVLCol=false |
5.9.4.5.1 明示的引数によって制御される問合せオプション
一部の問合せオプションは、Java APIのメソッドに対する明示的引数によって制御されます。
-
PgqlStatement
のexecuteQuery
メソッドには、タイムアウト(秒単位)、並列度、オプティマイザ動的サンプリング、および最大結果数の明示的引数があります。 -
translateQuery
メソッドには、並列性、オプティマイザ動的サンプリング、および最大結果数の明示的引数があります。また、PgqlPreparedStatement
には、executeQuery
およびtranslateQuery
用にこれらの同じ追加引数が用意されています。
例5-16 PgqlExample10.java
PgqlExample10.java
は、明示的引数によって制御される追加オプションを指定したPGQL問合せの実行を示しています。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to execute a PGQL query with various options.
*/
public class PgqlExample10
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute query to get a ResultSet object
String pgql =
"SELECT v1.\"fname\" AS fname1, v2.\"fname\" AS fname2 "+
"FROM MATCH (v1)-[:\"friendOf\"]->(v2)";
rs = ps.executeQuery(pgql /* query string */,
100 /* timeout (sec): 0 is default and implies no timeout */,
2 /* parallel: 1 is default */,
6 /* dynamic sampling: 2 is default */,
50 /* max results: -1 is default and implies no limit */,
"" /* options */);
// Print query results
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample10.java
は、test_graph
(GraphLoaderExample.javaコードを使用してロード可能)に対して次の出力を提供します。
+-----------------+ | FNAME1 | FNAME2 | +-----------------+ | Ray | Susan | | John | Susan | | Bill | John | | Susan | John | | John | Bill | +-----------------+
親トピック: PGQL変換および実行の追加オプション
5.9.4.5.2 GT$スケルトン表の使用
プロパティ・グラフ・リレーショナル・スキーマは、エッジが持っているプロパティの数に関係なく、グラフ内のエッジごとに1行を格納するGT$スケルトン表を定義します。デフォルトで、このスケルトン表にはデータは移入されているため、PGQL問合せ実行は、多くの場合、GT$表を利用して、GE$表でのソート操作を回避できます。それにより、大幅にパフォーマンスが改善されます。
executeQuery
およびtranslateQuery
のoptions
引数に"USE_GT_TAB=F"
を追加するか、Javaコマンドラインで-Doracle.pg.rdbms.pgql.useGtTab=false
を使用してGT$表の使用をオフにできます。
例5-17 PgqlExample11.java
PgqlExample11.java
は、GT$スケルトン表を使用する問合せを示します。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlSqlQueryTrans;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to avoid using the GT$ skeleton table for
* PGQL query execution.
*/
public class PgqlExample11
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute query to get a ResultSet object
String pgql =
"SELECT id(v1), id(v2) "+
"FROM MATCH (v1)-[knows]->(v2)";
// Get the SQL translation with GT table
PgqlSqlQueryTrans sqlTrans = ps.translateQuery(pgql,"");
// Print SQL translation
System.out.println("-- SQL Translation with GT Table ----------------------");
System.out.println(sqlTrans.getSqlTranslation());
// Get the SQL translation without GT table
sqlTrans = ps.translateQuery(pgql,"USE_GT_TAB=F");
// Print SQL translation
System.out.println("-- SQL Translation without GT Table -------------------------");
System.out.println(sqlTrans.getSqlTranslation());
}
finally {
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample11.java
は、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対して次の出力を提供します。
-- SQL Translation with GT Table ---------------------- SELECT 7 AS "id(v1)$T", to_nchar(T0$0.SVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v1)$V", T0$0.SVID AS "id(v1)$VN", to_timestamp_tz(null) AS "id(v1)$VT", 7 AS "id(v2)$T", to_nchar(T0$0.DVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v2)$V", T0$0.DVID AS "id(v2)$VN", to_timestamp_tz(null) AS "id(v2)$VT" FROM "SCOTT".TEST_GRAPHGT$ T0$0 -- SQL Translation without GT Table ------------------------- SELECT 7 AS "id(v1)$T", to_nchar(T0$0.SVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v1)$V", T0$0.SVID AS "id(v1)$VN", to_timestamp_tz(null) AS "id(v1)$VT", 7 AS "id(v2)$T", to_nchar(T0$0.DVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v2)$V", T0$0.DVID AS "id(v2)$VN", to_timestamp_tz(null) AS "id(v2)$VT" FROM (SELECT DISTINCT EID, SVID, DVID,EL FROM "SCOTT".TEST_GRAPHGE$) T0$0
親トピック: PGQL変換および実行の追加オプション
5.9.4.5.3 パス問合せのオプション
PGQLでパス問合せを実行するためのオプションがいくつかあります。Oracle SQLには、CONNECT BY句または再帰的WITH句という2つの基本的評価メソッドがあります。再帰的WITHがデフォルトの評価メソッドになります。さらに、再帰的WITH評価メソッドをさらに変更して、問合せ評価の再帰的ステップ中にDISTINCT修飾子を含めることができます。各ステップで個別の頂点を計算することにより、連結度が高いグラフでの組合せの激増を防止できます。DISTINCT修飾子は、データベース内に特定のパラメータ("_recursive_with_control"=8
)を設定する必要があるため、デフォルトでは追加されません。
検索するパスの最大長も制御できます。この場合のパスの長さは、*演算子と+演算子を評価する際に許可される反復数として定義されます。デフォルトの最大長は無制限です。
パスの評価オプションを要約すると次のようになります。
-
CONNECT BY: CONNECT BY句を使用するには、
options
引数に'USE_RW=F'
を指定するか、Javaコマンドラインに-Doracle.pg.rdbms.pgql.useRW=false
を指定します。 -
再帰的WITHでのDistinct修飾子:再帰的ステップでDISTINCT修飾子を使用するには、まずデータベース・セッションで
"_recursive_with_control"=8
を設定してから、options
引数に'USE_DIST_RW=T
'を指定するか、Javaコマンドラインに-Doracle.pg.rdbms.pgql.useDistRW=true
を指定します。セッションで"_recursive_with_control"
が8に設定されていない場合、「ORA-32486:再帰的WITH句の再帰的ブランチでのサポートされていない操作です」が発生します。 -
パス長の制限:nに対する*および+を評価する際の最大反復数を制限するには、問合せの
options
引数に'MAX_PATH_LEN=n'
を指定するか、Javaコマンドラインに-Doracle.pg.rdbms.pgql.maxPathLen=n
を指定します。
例5-18 PgqlExample12.java
PgqlExample12.java
は、様々なオプションでのパス問合せの変換を示しています。
import java.sql.Connection;
import java.sql.Statement;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlSqlQueryTrans;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to use various options with PGQL path queries.
*/
public class PgqlExample12
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Set "_recursive_with_control"=8 to enable distinct optimization
// optimization for recursive with
Statement stmt = conn.createStatement();
stmt.executeUpdate("alter session set \"_recursive_with_control\"=8");
stmt.close();
// Path Query to illustrate options
String pgql =
"PATH fof AS ()-[:\"friendOf\"]->() "+
"SELECT id(v1), id(v2) "+
"FROM MATCH (v1)-/:fof*/->(v2) "+
"WHERE id(v1) = 2";
// get SQL translation with defaults - Non-distinct Recursive WITH
PgqlSqlQueryTrans sqlTrans =
ps.translateQuery(pgql /* query string */,
2 /* parallel: default is 1 */,
2 /* dynamic sampling: default is 2 */,
-1 /* max results: -1 implies no limit */,
"" /* options */);
System.out.println("-- Default Path Translation --------------------");
System.out.println(sqlTrans.getSqlTranslation()+"\n");
// get SQL translation with DISTINCT reachability optimization
sqlTrans =
ps.translateQuery(pgql /* query string */,
2 /* parallel: default is 1 */,
2 /* dynamic sampling: default is 2 */,
-1 /* max results: -1 implies no limit */,
" USE_DIST_RW=T " /* options */);
System.out.println("-- DISTINCT RW Path Translation --------------------");
System.out.println(sqlTrans.getSqlTranslation()+"\n");
// get SQL translation with CONNECT BY
sqlTrans =
ps.translateQuery(pgql /* query string */,
2 /* parallel: default is 1 */,
2 /* dynamic sampling: default is 2 */,
-1 /* max results: -1 implies no limit */,
" USE_RW=F " /* options */);
System.out.println("-- CONNECT BY Path Translation --------------------");
System.out.println(sqlTrans.getSqlTranslation()+"\n");
}
finally {
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample12.java
は、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対して次の出力を提供します。
-- Default Path Translation -------------------- SELECT /*+ parallel(2) */ * FROM(SELECT 7 AS "id(v1)$T", to_nchar(T0$0.SVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v1)$V", T0$0.SVID AS "id(v1)$VN", to_timestamp_tz(null) AS "id(v1)$VT", 7 AS "id(v2)$T", to_nchar(T0$0.DVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v2)$V", T0$0.DVID AS "id(v2)$VN", to_timestamp_tz(null) AS "id(v2)$VT" FROM (/*Path[*/SELECT DISTINCT SVID, DVID FROM ( SELECT 2 AS SVID, 2 AS DVID FROM SYS.DUAL WHERE EXISTS( SELECT 1 FROM "SCOTT".TEST_GRAPHVT$ WHERE VID = 2) UNION ALL SELECT SVID,DVID FROM (WITH RW (ROOT, DVID) AS ( SELECT ROOT, DVID FROM (SELECT SVID ROOT, DVID FROM (SELECT T0$0.SVID AS SVID, T0$0.DVID AS DVID FROM "SCOTT".TEST_GRAPHGT$ T0$0 WHERE T0$0.SVID = 2 AND (T0$0.EL = n'friendOf' AND T0$0.EL IS NOT NULL)) ) UNION ALL SELECT RW.ROOT, R.DVID FROM (SELECT T0$0.SVID AS SVID, T0$0.DVID AS DVID FROM "SCOTT".TEST_GRAPHGT$ T0$0 WHERE (T0$0.EL = n'friendOf' AND T0$0.EL IS NOT NULL)) R, RW WHERE RW.DVID = R.SVID ) CYCLE DVID SET cycle_col TO 1 DEFAULT 0 SELECT ROOT SVID, DVID FROM RW))/*]Path*/) T0$0 WHERE T0$0.SVID = 2) -- DISTINCT RW Path Translation -------------------- SELECT /*+ parallel(2) */ * FROM(SELECT 7 AS "id(v1)$T", to_nchar(T0$0.SVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v1)$V", T0$0.SVID AS "id(v1)$VN", to_timestamp_tz(null) AS "id(v1)$VT", 7 AS "id(v2)$T", to_nchar(T0$0.DVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v2)$V", T0$0.DVID AS "id(v2)$VN", to_timestamp_tz(null) AS "id(v2)$VT" FROM (/*Path[*/SELECT DISTINCT SVID, DVID FROM ( SELECT 2 AS SVID, 2 AS DVID FROM SYS.DUAL WHERE EXISTS( SELECT 1 FROM "SCOTT".TEST_GRAPHVT$ WHERE VID = 2) UNION ALL SELECT SVID,DVID FROM (WITH RW (ROOT, DVID) AS ( SELECT ROOT, DVID FROM (SELECT SVID ROOT, DVID FROM (SELECT T0$0.SVID AS SVID, T0$0.DVID AS DVID FROM "SCOTT".TEST_GRAPHGT$ T0$0 WHERE T0$0.SVID = 2 AND (T0$0.EL = n'friendOf' AND T0$0.EL IS NOT NULL)) ) UNION ALL SELECT DISTINCT RW.ROOT, R.DVID FROM (SELECT T0$0.SVID AS SVID, T0$0.DVID AS DVID FROM "SCOTT".TEST_GRAPHGT$ T0$0 WHERE (T0$0.EL = n'friendOf' AND T0$0.EL IS NOT NULL)) R, RW WHERE RW.DVID = R.SVID ) CYCLE DVID SET cycle_col TO 1 DEFAULT 0 SELECT ROOT SVID, DVID FROM RW))/*]Path*/) T0$0 WHERE T0$0.SVID = 2) -- CONNECT BY Path Translation -------------------- SELECT /*+ parallel(2) */ * FROM(SELECT 7 AS "id(v1)$T", to_nchar(T0$0.SVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v1)$V", T0$0.SVID AS "id(v1)$VN", to_timestamp_tz(null) AS "id(v1)$VT", 7 AS "id(v2)$T", to_nchar(T0$0.DVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v2)$V", T0$0.DVID AS "id(v2)$VN", to_timestamp_tz(null) AS "id(v2)$VT" FROM (/*Path[*/SELECT DISTINCT SVID, DVID FROM ( SELECT 2 AS SVID, 2 AS DVID FROM SYS.DUAL WHERE EXISTS( SELECT 1 FROM "SCOTT".TEST_GRAPHVT$ WHERE VID = 2) UNION ALL SELECT SVID, DVID FROM (SELECT CONNECT_BY_ROOT T0$0.SVID AS SVID, T0$0.DVID AS DVID FROM( SELECT T0$0.SVID AS SVID, T0$0.DVID AS DVID FROM "SCOTT".TEST_GRAPHGT$ T0$0 WHERE (T0$0.EL = n'friendOf' AND T0$0.EL IS NOT NULL)) T0$0 START WITH T0$0.SVID = 2 CONNECT BY NOCYCLE PRIOR DVID = SVID))/*]Path*/) T0$0 WHERE T0$0.SVID = 2)
デフォルトの再帰的WITH戦略を使用する最初の問合せの問合せ計画は、次のようになります。
-- default RW --------------------------------------------------------------------------------------- | Id | Operation | Name | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | TEMP TABLE TRANSFORMATION | | | 2 | LOAD AS SELECT (CURSOR DURATION MEMORY) | SYS_TEMP_0FD9D6662_37AA44 | | 3 | UNION ALL (RECURSIVE WITH) BREADTH FIRST | | | 4 | PX COORDINATOR | | | 5 | PX SEND QC (RANDOM) | :TQ20000 | | 6 | LOAD AS SELECT (CURSOR DURATION MEMORY) | SYS_TEMP_0FD9D6662_37AA44 | | 7 | PX PARTITION HASH ALL | | |* 8 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | TEST_GRAPHGT$ | |* 9 | INDEX RANGE SCAN | TEST_GRAPHXSG$ | | 10 | PX COORDINATOR | | | 11 | PX SEND QC (RANDOM) | :TQ10000 | | 12 | LOAD AS SELECT (CURSOR DURATION MEMORY) | SYS_TEMP_0FD9D6662_37AA44 | | 13 | NESTED LOOPS | | | 14 | PX BLOCK ITERATOR | | |* 15 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6662_37AA44 | | 16 | PARTITION HASH ALL | | |* 17 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | TEST_GRAPHGT$ | |* 18 | INDEX RANGE SCAN | TEST_GRAPHXSG$ | | 19 | PX COORDINATOR | | | 20 | PX SEND QC (RANDOM) | :TQ30001 | | 21 | VIEW | | | 22 | HASH UNIQUE | | | 23 | PX RECEIVE | | | 24 | PX SEND HASH | :TQ30000 | | 25 | HASH UNIQUE | | | 26 | VIEW | | | 27 | UNION-ALL | | | 28 | PX SELECTOR | | |* 29 | FILTER | | | 30 | FAST DUAL | | | 31 | PARTITION HASH SINGLE | | |* 32 | INDEX SKIP SCAN | TEST_GRAPHXQV$ | | 33 | VIEW | | |* 34 | VIEW | | | 35 | PX BLOCK ITERATOR | | | 36 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6662_37AA44 | ---------------------------------------------------------------------------------------
再帰的ステップにDISTINCT修飾子を追加する2番目の問合せの問合せ計画は、次のようになります。
-------------------------------------------------------------------------------------------- | Id | Operation | Name | -------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | TEMP TABLE TRANSFORMATION | | | 2 | LOAD AS SELECT (CURSOR DURATION MEMORY) | SYS_TEMP_0FD9D6669_37AA44 | | 3 | UNION ALL (RECURSIVE WITH) BREADTH FIRST | | | 4 | PX COORDINATOR | | | 5 | PX SEND QC (RANDOM) | :TQ20000 | | 6 | LOAD AS SELECT (CURSOR DURATION MEMORY) | SYS_TEMP_0FD9D6669_37AA44 | | 7 | PX PARTITION HASH ALL | | |* 8 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | TEST_GRAPHGT$ | |* 9 | INDEX RANGE SCAN | TEST_GRAPHXSG$ | | 10 | PX COORDINATOR | | | 11 | PX SEND QC (RANDOM) | :TQ10001 | | 12 | LOAD AS SELECT (CURSOR DURATION MEMORY) | SYS_TEMP_0FD9D6669_37AA44 | | 13 | SORT GROUP BY | | | 14 | PX RECEIVE | | | 15 | PX SEND HASH | :TQ10000 | | 16 | SORT GROUP BY | | | 17 | NESTED LOOPS | | | 18 | PX BLOCK ITERATOR | | |* 19 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6669_37AA44 | | 20 | PARTITION HASH ALL | | |* 21 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | TEST_GRAPHGT$ | |* 22 | INDEX RANGE SCAN | TEST_GRAPHXSG$ | | 23 | PX COORDINATOR | | | 24 | PX SEND QC (RANDOM) | :TQ30001 | | 25 | VIEW | | | 26 | HASH UNIQUE | | | 27 | PX RECEIVE | | | 28 | PX SEND HASH | :TQ30000 | | 29 | HASH UNIQUE | | | 30 | VIEW | | | 31 | UNION-ALL | | | 32 | PX SELECTOR | | |* 33 | FILTER | | | 34 | FAST DUAL | | | 35 | PARTITION HASH SINGLE | | |* 36 | INDEX SKIP SCAN | TEST_GRAPHXQV$ | | 37 | VIEW | | |* 38 | VIEW | | | 39 | PX BLOCK ITERATOR | | | 40 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6669_37AA44 | --------------------------------------------------------------------------------------------
CONNECTY BYを使用する3番目の問合せの問合せ計画は、次のようになります。
----------------------------------------------------------------------------- | Id | Operation | Name | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | VIEW | | | 2 | HASH UNIQUE | | | 3 | VIEW | | | 4 | UNION-ALL | | |* 5 | FILTER | | | 6 | FAST DUAL | | | 7 | PARTITION HASH SINGLE | | |* 8 | INDEX SKIP SCAN | TEST_GRAPHXQV$ | |* 9 | VIEW | | |* 10 | CONNECT BY WITH FILTERING | | | 11 | PX COORDINATOR | | | 12 | PX SEND QC (RANDOM) | :TQ10000 | | 13 | PX PARTITION HASH ALL | | |* 14 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| TEST_GRAPHGT$ | |* 15 | INDEX RANGE SCAN | TEST_GRAPHXSG$ | | 16 | NESTED LOOPS | | | 17 | CONNECT BY PUMP | | | 18 | PARTITION HASH ALL | | |* 19 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | TEST_GRAPHGT$ | |* 20 | INDEX RANGE SCAN | TEST_GRAPHXSG$ | -----------------------------------------------------------------------------
例5-19 PgqlExample13.java
PgqlExample13.java
は、パス問合せ評価中に長さ制限を設定する方法を示しています。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to use the maximum path length option for
* PGQL path queries.
*/
public class PgqlExample13
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Path Query to illustrate options
String pgql =
"PATH fof AS ()-[:\"friendOf\"]->() "+
"SELECT v1.\"fname\" AS fname1, v2.\"fname\" AS fname2 "+
"FROM MATCH (v1)-/:fof*/->(v2) "+
"WHERE v1.\"fname\" = 'Ray'";
// execute query for 1-hop
rs = ps.executeQuery(pgql, " MAX_PATH_LEN=1 ");
// print results
System.out.println("-- Results for 1-hop ----------------");
rs.print();
// close result set
rs.close();
// execute query for 2-hop
rs = ps.executeQuery(pgql, " MAX_PATH_LEN=2 ");
// print results
System.out.println("-- Results for 2-hop ----------------");
rs.print();
// close result set
rs.close();
// execute query for 3-hop
rs = ps.executeQuery(pgql, " MAX_PATH_LEN=3 ");
// print results
System.out.println("-- Results for 3-hop ----------------");
rs.print();
// close result set
rs.close();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample13.java
には、test_graph
(GraphLoaderExample.java
コードを使用してロード可能)に対する次の出力があります。
-- Results for 1-hop ---------------- +-----------------+ | FNAME1 | FNAME2 | +-----------------+ | Ray | Ray | | Ray | Susan | +-----------------+ -- Results for 2-hop ---------------- +-----------------+ | FNAME1 | FNAME2 | +-----------------+ | Ray | Susan | | Ray | Ray | | Ray | John | +-----------------+ -- Results for 3-hop ---------------- +-----------------+ | FNAME1 | FNAME2 | +-----------------+ | Ray | Susan | | Ray | Bill | | Ray | Ray | | Ray | John | +-----------------+
親トピック: PGQL変換および実行の追加オプション
5.9.4.5.4 Partialオブジェクトの構築のオプション
問合せ結果からエッジを読み込んでいるとき、開始および終了頂点をローカル・キャッシュに追加する際に、次の2つ動作が可能です。
-
エッジ自体から入手可能な頂点IDのみを追加します。このオプションは、効率化のためのデフォルトです。
-
項点IDを追加し、開始および終了頂点のすべてのプロパティを取得します。この動作では、PGQL問合せ結果セットから作成された各
OracleVertex
オブジェクトでsetPartial(true)
を呼び出せます。
例5-20 PgqlExample14.java
PgqlExample14.java
は、この動作の違いを示しています。このプログラムは、まずすべてのエッジを取得するための問合せを実行します。それにより、インシデントの頂点がローカル・キャッシュに追加されます。2番目の問合せはすべての頂点を取得します。次に、プログラムは、各OracleVertex
オブジェクトを表示して、どのプロパティがロードされているかを示します。
import java.sql.Connection;
import oracle.pg.rdbms.Oracle;
import oracle.pg.rdbms.OraclePropertyGraph;
import oracle.pg.rdbms.OracleVertex;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows the behavior of setPartial(true) for OracleVertex objects
* created from PGQL query results.
*/
public class PgqlExample14
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
Oracle oracle = null;
OraclePropertyGraph opg = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Query to illustrate set partial
String pgql =
"SELECT id(e), label(e) "+
"FROM MATCH (v1)-[e:\"knows\"]->(v2)";
// execute query
rs = ps.executeQuery(pgql, " ");
// print results
System.out.println("-- Results for edge query -----------------");
rs.print();
// close result set
rs.close();
// Create an Oracle Property Graph instance
oracle = new Oracle(conn);
opg = OraclePropertyGraph.getInstance(oracle,graph);
// Query to retrieve vertices
pgql =
"SELECT id(v) "+
"FROM MATCH (v)";
// Get each vertex object in result and print with toString()
rs = ps.executeQuery(pgql, " ");
// iterate through result
System.out.println("-- Vertex objects retrieved from vertex query --");
while (rs.next()) {
Long vid = rs.getLong(1);
OracleVertex v = OracleVertex.getInstance(opg, vid);
System.out.println(v.toString());
}
// close result set
rs.close();
// Execute the same query but call setPartial(true) for each vertex
rs = ps.executeQuery(pgql, " ");
System.out.println("-- Vertex objects retrieved from vertex query with setPartial(true) --");
while (rs.next()) {
Long vid = rs.getLong(1);
OracleVertex v = OracleVertex.getInstance(opg, vid);
v.setPartial(true);
System.out.println(v.toString());
}
// close result set
rs.close();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
// close the property graph
if (opg != null) {
opg.close();
}
// close oracle
if (oracle != null) {
oracle.dispose();
}
}
}
}
PgqlExample14.java
の出力(GraphLoaderExample.java
コードを使用してロードできます)は、次のとおりです。
-- Results for edge query ----------------- +------------------+ | id(e) | label(e) | +------------------+ | 6 | knows | | 11 | knows | | 10 | knows | | 5 | knows | | 4 | knows | | 13 | knows | | 9 | knows | | 12 | knows | | 8 | knows | | 7 | knows | | 14 | knows | | 15 | knows | +------------------+ -- Vertex objects retrieved from vertex query -- Vertex ID 3 [NULL] {} Vertex ID 0 [NULL] {} Vertex ID 2 [NULL] {} Vertex ID 1 [NULL] {} -- Vertex objects retrieved from vertex query with setPartial(true) -- Vertex ID 3 [NULL] {bval:bol:false, fname:str:Susan, lname:str:Blue, mval:bol:false, age:int:35} Vertex ID 0 [NULL] {bval:bol:true, fname:str:Bill, lname:str:Brown, mval:str:y, age:int:40} Vertex ID 2 [NULL] {fname:str:Ray, lname:str:Green, mval:dat:1985-01-01 04:00:00.0, age:int:41} Vertex ID 1 [NULL] {bval:bol:true, fname:str:John, lname:str:Black, mval:int:27, age:int:30}
親トピック: PGQL変換および実行の追加オプション
5.9.4.6 別のユーザーのプロパティ・グラフの問合せ
データベースの適切な権限を付与されている場合は、別のユーザーのプロパティ・グラフのデータを問い合せることができます。たとえば、SCOTTのスキーマでGRAPH1を問い合せるには、SCOTT.GRAPH1GE$、SCOTT.GRAPH1VT$、SCOTT.GRAPH1GT$およびSCOTT.GRAPH1VD$に対するREAD権限が必要です。
例5-21 PgqlExample15.java
PgqlExample15.java
は、他のユーザーがSCOTTのスキーマ内のグラフを問い合せる方法を示しています。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to query a property graph located in another user's
* schema. READ privilege on GE$, VT$, GT$ and VD$ tables for the other user's
* property graph are required to avoid ORA-00942: table or view does not exist.
*/
public class PgqlExample15
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Set schema so that we can query Scott's graph
pgqlConn.setSchema("SCOTT");
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute query to get a ResultSet object
String pgql =
"SELECT v.\"fname\" AS fname, v.\"lname\" AS lname "+
"FROM MATCH (v)";
rs = ps.executeQuery(pgql, "");
// Print query results
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
次のSQL文は、データベース・ユーザーUSER2を作成し、必要な権限を付与します。また、OraclePropertyGraph.grantAccess
Java APIを使用しても同じ効果を得ることができます。
SQL> grant connect, resource, unlimited tablespace to user2 identified by user2; Grant succeeded. SQL> grant read on scott.test_graphvt$ to user2; Grant succeeded. SQL> grant read on scott.test_graphge$ to user2; Grant succeeded. SQL> grant read on scott.test_graphgt$ to user2; Grant succeeded. SQL> grant read on scott.test_graphvd$ to user2; Grant succeeded.
データベースにUSER2として接続した場合のtest_graph
データ・セットのPgqlExample15.java
の出力は次のようになります。PgqlExample15
を実行する前に、test_graph
は(GraphLoaderExample.java
コードを使用して)ユーザーSCOTTによってGRAPH1としてすでにロードされている必要があることに注意してください。
+---------------+ | FNAME | LNAME | +---------------+ | Susan | Blue | | Bill | Brown | | Ray | Green | | John | Black | +---------------+
5.9.4.7 PGQLでの問合せオプティマイザ・ヒントの使用
Java APIでは、PGQL問合せを実行する際の結合タイプに影響を与える問合せオプティマイザ・ヒントが許可されます。PgqlStatement
およびPgqlPreparedStatement
内のexecuteQuery
メソッドおよびtranslateQuery
メソッドは、対応するSQL問合せの問合せ計画に影響を与えるために、options引数で次の文字列を受け入れます。
-
ALL_EDGE_NL – $GE表と$GT表に関連するすべての結合にネステッド・ループ結合を使用します。
-
ALL_EDGE_HASH – $GE表と$GT表に関連するすべての結合にHASH結合を使用します。
-
ALL_VERTEX_NL – $VT表に関連するすべての結合にネステッド・ループ結合を使用します。
-
ALL_VERTEX_HASH – $VT表に関連するすべての結合にHASH結合を使用します。
例5-22 PgqlExample16.java
PgqlExample16.java
は、オプティマイザ・ヒントを使用して、グラフ・トラバースに使用される結合に影響を与える方法を示しています。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlSqlQueryTrans;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to use query optimizer hints with PGQL queries.
*/
public class PgqlExample16
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Query to illustrate join hints
String pgql =
"SELECT id(v1), id(v4) "+
"FROM MATCH (v1)-[:\"friendOf\"]->(v2)-[:\"friendOf\"]->(v3)-[:\"friendOf\"]->(v4)";
// get SQL translation with hash join hint
PgqlSqlQueryTrans sqlTrans =
ps.translateQuery(pgql /* query string */,
" ALL_EDGE_HASH " /* options */);
// print SQL translation
System.out.println("-- Query with ALL_EDGE_HASH --------------------");
System.out.println(sqlTrans.getSqlTranslation()+"\n");
// get SQL translation with nested loop join hint
sqlTrans =
ps.translateQuery(pgql /* query string */,
" ALL_EDGE_NL " /* options */);
// print SQL translation
System.out.println("-- Query with ALL_EDGE_NL ---------------------");
System.out.println(sqlTrans.getSqlTranslation()+"\n");
}
finally {
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
test_graph
のPgqlExample16.java
の出力(GraphLoaderExample.java
コードを使用してロードできます)は、次のとおりです。
-- Query with ALL_EDGE_HASH -------------------- SELECT /*+ USE_HASH(T0$0 T0$1 T0$2) */ 7 AS "id(v1)$T", to_nchar(T0$0.SVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v1)$V", T0$0.SVID AS "id(v1)$VN", to_timestamp_tz(null) AS "id(v1)$VT", 7 AS "id(v4)$T", to_nchar(T0$2.DVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v4)$V", T0$2.DVID AS "id(v4)$VN", to_timestamp_tz(null) AS "id(v4)$VT" FROM "SCOTT".TEST_GRAPHGT$ T0$0, "SCOTT".TEST_GRAPHGT$ T0$1, "SCOTT".TEST_GRAPHGT$ T0$2 WHERE T0$0.DVID=T0$1.SVID AND T0$1.DVID=T0$2.SVID AND (T0$0.EL = n'friendOf' AND T0$0.EL IS NOT NULL) AND (T0$1.EL = n'friendOf' AND T0$1.EL IS NOT NULL) AND (T0$2.EL = n'friendOf' AND T0$2.EL IS NOT NULL) -- Query with ALL_EDGE_NL --------------------- SELECT /*+ USE_NL(T0$0 T0$1 T0$2) */ 7 AS "id(v1)$T", to_nchar(T0$0.SVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v1)$V", T0$0.SVID AS "id(v1)$VN", to_timestamp_tz(null) AS "id(v1)$VT", 7 AS "id(v4)$T", to_nchar(T0$2.DVID,'TM9','NLS_Numeric_Characters=''.,''') AS "id(v4)$V", T0$2.DVID AS "id(v4)$VN", to_timestamp_tz(null) AS "id(v4)$VT" FROM "SCOTT".TEST_GRAPHGT$ T0$0, "SCOTT".TEST_GRAPHGT$ T0$1, "SCOTT".TEST_GRAPHGT$ T0$2 WHERE T0$0.DVID=T0$1.SVID AND T0$1.DVID=T0$2.SVID AND (T0$0.EL = n'friendOf' AND T0$0.EL IS NOT NULL) AND (T0$1.EL = n'friendOf' AND T0$1.EL IS NOT NULL) AND (T0$2.EL = n'friendOf' AND T0$2.EL IS NOT NULL)
ALL_EDGE_HASHを使用する最初の問合せの問合せ計画は次のようになります。
----------------------------------------------- | Id | Operation | Name | ----------------------------------------------- | 0 | SELECT STATEMENT | | |* 1 | HASH JOIN | | |* 2 | HASH JOIN | | | 3 | PARTITION HASH ALL | | |* 4 | TABLE ACCESS FULL | TEST_GRAPHGT$ | | 5 | PARTITION HASH ALL | | |* 6 | TABLE ACCESS FULL | TEST_GRAPHGT$ | | 7 | PARTITION HASH ALL | | |* 8 | TABLE ACCESS FULL | TEST_GRAPHGT$ | -----------------------------------------------
ALL_EDGE_NLを使用する2番目の問合せの問合せ計画は次のようになります。
----------------------------------------------------------------------- | Id | Operation | Name | ----------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | NESTED LOOPS | | | 2 | NESTED LOOPS | | | 3 | PARTITION HASH ALL | | |* 4 | TABLE ACCESS FULL | TEST_GRAPHGT$ | | 5 | PARTITION HASH ALL | | |* 6 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| TEST_GRAPHGT$ | |* 7 | INDEX RANGE SCAN | TEST_GRAPHXSG$ | | 8 | PARTITION HASH ALL | | |* 9 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | TEST_GRAPHGT$ | |* 10 | INDEX RANGE SCAN | TEST_GRAPHXSG$ | -----------------------------------------------------------------------
5.9.5 INSERT、UPDATEおよびDELETE文によるプロパティ・グラフの変更
PGQLは、プロパティ・グラフでのINSERT、UPDATEおよびDELETE操作をサポートしています。このようなDML操作は、PgqlStatement
のメソッドexecute
によって実行できます。このトピックでは、このような操作の例をいくつか示します。
ノート:
INSERT、UPDATEおよびDELETE文を実行できるようにするには、JDBC接続の自動コミットをオフにする必要があります。
例5-23 PgqlExample17.java (挿入)
PgqlExample17.java
は、複数の頂点とエッジをグラフに挿入します。頂点およびエッジのID値の定義には、特殊なプロパティ_ora_id
が使用されていることに注意してください。プロパティ_ora_id
を省略すると、グラフに挿入される新しい頂点またはエッジごとに一意のIDが生成されます。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to execute a PGQL INSERT operation.
*/
public class PgqlExample17
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
conn.setAutoCommit(false);
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute insert statement
String pgql =
"INSERT VERTEX p1 LABELS (person) PROPERTIES (p1.\"_ora_id\" = 1, p1.fname = 'Jake') "+
" , VERTEX p2 LABELS (person) PROPERTIES (p2.\"_ora_id\" = 2, p2.fname = 'Amy') "+
" , VERTEX p3 LABELS (person) PROPERTIES (p3.\"_ora_id\" = 3, p3.fname = 'Erik') "+
" , VERTEX p4 LABELS (person) PROPERTIES (p4.\"_ora_id\" = 4, p4.fname = 'Jane') "+
" , EDGE e1 BETWEEN p1 AND p2 LABELS (knows) PROPERTIES (e1.\"_ora_id\" = 1, e1.since = DATE '2003-04-21') "+
" , EDGE e2 BETWEEN p1 AND p3 LABELS (knows) PROPERTIES (e2.\"_ora_id\" = 2, e2.since = DATE '2010-02-10') "+
" , EDGE e3 BETWEEN p3 AND p4 LABELS (knows) PROPERTIES (e3.\"_ora_id\" = 3, e3.since = DATE '1999-01-03') ";
ps.execute(pgql, /* query string */
"", /* query options */
"" /* modify options */);
// Execute a query to verify insertion
pgql =
" SELECT id(p1) AS id1, p1.fname AS person1, id(p2) as id2, p2.fname AS person2, id(e) as e, e.since "+
" FROM MATCH (p1)-[e:knows]->(p2) "+
"ORDER BY id1, id2";
rs = ps.executeQuery(pgql, "");
// Print the results
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample17.java
の出力は次のとおりです。
+-----------------------------------------------------------+ | ID1 | PERSON1 | ID2 | PERSON2 | E | SINCE | +-----------------------------------------------------------+ | 1 | Jake | 2 | Amy | 1 | 2003-04-20 17:00:00.0 | | 1 | Jake | 3 | Erik | 2 | 2010-02-09 16:00:00.0 | | 3 | Erik | 4 | Jane | 3 | 1999-01-02 16:00:00.0 | +-----------------------------------------------------------+
INSERT文のその他の例については、こちらのPGQL仕様の関連するセクションを参照してください。
例5-24 PgqlExample18.java (更新)
PgqlExample18.java
は、UPDATE文のFROM句で一致する頂点とエッジのいくつかのプロパティを更新します。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to execute a PGQL UPDATE operation.
*/
public class PgqlExample18
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
conn.setAutoCommit(false);
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute update statement
String pgql =
"UPDATE p1 SET (p1.age = 47, p1.lname = 'Red'), "+
" p2 SET (p2.age = 29, p2.lname = 'White'), "+
" e SET (e.strength = 100) "+
"FROM MATCH (p1) -[e:knows]-> (p2) "+
"WHERE p1.fname = 'Jake' AND p2.fname = 'Amy'";
ps.execute(pgql, /* query string */
"", /* query options */
"" /* modify options */);
// Execute a query to verify update
pgql =
"SELECT p1.fname AS fname1, p1.lname AS lname1, p1.age AS age1, "+
" p2.fname AS fname2, p2.lname AS lname2, p2.age AS age2, e.strength "+
"FROM MATCH (p1) -[e:knows]-> (p2)";
rs = ps.executeQuery(pgql, "");
// Print the results
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
以前PgqlExample17.java
が実行されたグラフに適用されたPgqlExample18.java
の出力を次に示します。
+----------------------------------------------------------------+ | FNAME1 | LNAME1 | AGE1 | FNAME2 | LNAME2 | AGE2 | STRENGTH | +----------------------------------------------------------------+ | Jake | Red | 47 | Amy | White | 29 | 100 | | Jake | Red | 47 | Erik | <null> | <null> | <null> | | Erik | <null> | <null> | Jane | <null> | <null> | <null> | +----------------------------------------------------------------+
UPDATE文のその他の例については、こちらのPGQL仕様の関連するセクションを参照してください。
例5-25 PgqlExample19.java (削除)
PgqlExample19.java
は、DELETE文のFROM句と一致するエッジを削除します。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to execute a PGQL DELETE operation.
*/
public class PgqlExample19
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
conn.setAutoCommit(false);
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute delete statement
String pgql =
"DELETE e "+
" FROM MATCH (p1) -[e:knows]-> (p2) "+
" WHERE p1.fname = 'Jake'";
ps.execute(pgql, /* query string */
"", /* query options */
"" /* modify options */);
// Execute a query to verify delete
pgql =
"SELECT p1.fname AS fname1, p2.fname AS fname2 "+
" FROM MATCH (p1) -[e:knows]-> (p2)";
rs = ps.executeQuery(pgql, "");
// Print the results
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
以前PgqlExample18.java
が実行されたグラフに適用されたPgqlExample19.java
の出力を次に示します。
+-----------------+ | FNAME1 | FNAME2 | +-----------------+ | Erik | Jane | +-----------------+
DELETE文のその他の例については、こちらのPGQL仕様の関連するセクションを参照してください。
例5-26 PgqlExample20.java (複数の変更)
PgqlExample20.java
は、同じ文で複数の変更を実行します。エッジの挿入、頂点プロパティの更新、別のエッジの削除が行われます。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to execute a PGQL
* INSERT/UPDATE/DELETE operation.
*/
public class PgqlExample20
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
conn.setAutoCommit(false);
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Execute INSERT/UPDATE/DELETE statement
String pgql =
"INSERT EDGE f BETWEEN p2 AND p1 LABELS (knows) PROPERTIES (f.since = e.since) "+
"UPDATE p1 SET (p1.age = 30) "+
" , p2 SET (p2.age = 25) "+
"DELETE e "+
" FROM MATCH (p1) -[e:knows]-> (p2) "+
" WHERE p1.fname = 'Erik'";
ps.execute(pgql, /* query string */
"", /* query options */
"" /* modify options */);
// Execute a query to verify INSERT/UPDATE/DELETE
pgql =
"SELECT p1.fname AS fname1, p1.age AS age1, "+
" p2.fname AS fname2, p2.age AS age2, e.since "+
" FROM MATCH (p1) -[e:knows]-> (p2)";
rs = ps.executeQuery(pgql, "");
// Print the results
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
以前PgqlExample19.java
が実行されたグラフに適用されたPgqlExample20.java
の出力を次に示します。
+-------------------------------------------------------+ | FNAME1 | AGE1 | FNAME2 | AGE2 | SINCE | +-------------------------------------------------------+ | Jane | 25 | Erik | 30 | 1999-01-02 16:00:00.0 | +-------------------------------------------------------+
INSERT、UPDATE、DELETE文のその他の例については、こちらのPGQL仕様の関連するセクションを参照してください。
5.9.5.1 PGQL文実行の追加オプション
PGQL文の実行に影響を与える複数のオプションがあります。問合せオプションを設定する主な方法は次のとおりです。
execute
のmodify options
に対して文字列引数のフラグを使用する- Java JVM引数を使用する
次の表に、PGQL文の実行を変更するための主なオプションをまとめます。
表5-3 PGQL文の変更オプション
オプション | デフォルト | オプション・フラグ | JVM引数 |
---|---|---|---|
自動コミット | JDBC自動コミットがオフの場合はtrue、JDBC自動コミットがオンの場合はfalse | AUTO_COMMIT=F | -Doracle.pg.rdbms.pgql.autoCommit=false |
カスケードの削除 | true | DELETE_CASCADE=F | -Doracle.pg.rdbms.pgql.deleteCascade=false |
5.9.5.1.1 PGQL自動コミットの無効化
INSERT、UPDATEまたはDELETE操作が実行されると、PGQL実行の最後にコミットが自動的に実行され、RDBMS側で変更が持続されます。
フラグAUTO_COMMIT=F
をexecute
のoptions
引数に追加するか、フラグDoracle.pg.rdbms.pgql.autoCommit=false
をJavaコマンド・ラインで設定して、自動コミットをオフにすることができます。自動コミットがオフの場合は、グラフの変更を持続または取り消すために、JDBC接続で必要なコミットまたはロールバックを実行する必要があります。
例5-27 自動コミットおよびロールバック変更の無効化
PgqlExample21.java
は、自動コミットをオフにして、変更のロールバックを実行します。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows how to modify a PGQL graph
* with auto commit off.
*/
public class PgqlExample21
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
PgqlResultSet rs = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
conn.setAutoCommit(false);
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Delete all the edges in the graph
String pgql =
"DELETE e "+
" FROM MATCH () -[e]-> ()";
ps.execute(pgql, /* query string */
"", /* query options */
"AUTO_COMMIT=F" /* modify options */);
// Execute a query to verify deletion
pgql =
"SELECT COUNT(e) "+
" FROM MATCH () -[e]-> ()";
rs = ps.executeQuery(pgql, "");
// Print the results
System.out.println("Number of edges after deletion:");
rs.print();
rs.close();
// Rollback the changes. This is possible because
// AUTO_COMMIT=F flag was used in execute
conn.rollback();
// Execute a query to verify rollback
pgql =
"SELECT COUNT(e) "+
" FROM MATCH () -[e]-> ()";
rs = ps.executeQuery(pgql, "");
// Print the results
System.out.println("Number of edges after rollback:");
rs.print();
}
finally {
// close the result set
if (rs != null) {
rs.close();
}
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample21.java
では、エッジが1つあるグラフの場合、次に示す出力が得られます。
Number of edges after deletion: +----------+ | COUNT(e) | +----------+ | 0 | +----------+ Number of edges after rollback: +----------+ | COUNT(e) | +----------+ | 1 | +----------+
親トピック: PGQL文実行の追加オプション
5.9.5.1.2 カスケード削除の無効化
グラフから頂点を削除すると、その入力と出力のエッジもすべて自動的に削除されます。
フラグを設定するexecute
のoptions
引数でフラグDELETE_CASCADE=F
を使用するか、Javaコマンド・ラインでフラグDoracle.pg.rdbms.pgql.autoCommit=false
を設定すると、カスケード削除をオフにできます。入力エッジまたは出力エッジのある頂点が削除され、カスケード削除がオフになっている場合は、実行しようとしている安全でない操作について警告するエラーがスローされます。
例5-28 カスケード削除の無効化
PgqlExample22.java
は、カスケード削除がオフの場合に出力エッジのある頂点を削除しようとします。
import java.sql.Connection;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlStatement;
import oracle.pg.rdbms.pgql.PgqlToSqlException;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;
/**
* This example shows the use of DELETE_CASCADE flag.
*/
public class PgqlExample22
{
public static void main(String[] args) throws Exception
{
int idx=0;
String host = args[idx++];
String port = args[idx++];
String sid = args[idx++];
String user = args[idx++];
String password = args[idx++];
String graph = args[idx++];
Connection conn = null;
PgqlStatement ps = null;
try {
//Get a jdbc connection
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@"+host+":"+port +":"+sid);
pds.setUser(user);
pds.setPassword(password);
conn = pds.getConnection();
conn.setAutoCommit(false);
// Get a PGQL connection
PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
pgqlConn.setGraph(graph);
// Create a PgqlStatement
ps = pgqlConn.createStatement();
// Delete all the vertices with output edges
// This will throw an error
String pgql =
"DELETE v "+
" FROM MATCH (v) -[e]-> ()";
ps.execute(pgql, /* query string */
"", /* query options */
"DELETE_CASCADE=F" /* modify options */);
}
catch (PgqlToSqlException ex){
System.out.println("Error in execution: " + ex.getMessage());
}
finally {
// close the statement
if (ps != null) {
ps.close();
}
// close the connection
if (conn != null) {
conn.close();
}
}
}
}
PgqlExample22.java
では、少なくともエッジが1つあるグラフの場合、次に示す出力が得られます。
Error in execution: Attempting to delete vertices with incoming/outgoing edges. Drop edges first or turn on DELETE_CASCADE option
親トピック: PGQL文実行の追加オプション
5.9.6 PGQL問合せでのパフォーマンスの考慮事項
多数の要因が、Oracle DatabaseでのPGQL問合せのパフォーマンスに影響を与えます。次に、問合せのパフォーマンスのためのお薦めの慣例をいくつか示します。
問合せオプティマイザ統計
優れた、最新の問合せオプティマイザ統計は、問合せのパフォーマンスにとって非常に重要です。プロパティ・グラフのデータを大幅に更新した後は、必ずOPG_APIS.ANALYZE_PGを実行してください。
パラレル問合せの実行
パラレル問合せ実行を使用して、OracleのパラレルSQLエンジンを利用します。多くの場合、パラレル実行は、シリアル実行に対して大幅なスピードアップになります。パラレル実行は、再帰的WITH戦略を使用して評価されるパス問合せで特に重要となります。
パラレル問合せ実行の詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』も参照してください。
オプティマイザの動的サンプリング
グラフ・データ・モデル本来の柔軟性のため、静的情報では必ずしも最適な問合せ計画が生成されない場合があります。そのような場合は、よりすぐれた問合せ計画を得るために、問合せオプティマイザで動的サンプリングを使用して、実行時にデータのサンプリングを実行できます。サンプリングされるデータ量は、使用される動的サンプリング・レベルによって制御されます。動的サンプリング・レベルの範囲は0から11です。最適な使用レベルは、特定のデータセットとワークロードによって異なりますが、多くの場合、2 (デフォルト)、6、または11のレベルで良好な結果を得られます。
『Oracle Database SQLチューニング・ガイド』の「補足的な動的統計」も参照してください。
バインド変数
可能な場合は常に、定数にバインド変数を使用します。バインド変数の使用により、問合せのコンパイル時間が大幅に削減されます。それにより、使用されている定数値のみが異なる問合せでの問合せワークロードのスループットが大幅に増加します。さらに、バインド変数を使用した問合せでは、インジェクション攻撃に対する脆弱性が軽減されます。
パス問合せ
+
(プラス記号)演算子または*
(アスタリスク)演算子を使用して任意の長さのパスを検索する、PGQLのパス問合せでは、計算が非常に複雑なため、特別な考慮が必要になります。最適なパフォーマンスを得るためには、パラレル実行を使用し、再帰的WITH(USE_DIST_RW=T)にDISTINCTオプションを使用してください。また、大規模で、連結度が高いグラフの場合は、MAX_PATH_LEN=nを使用して、再帰ステップの反復数を妥当な数に制限することをお薦めします。お薦めの方法は、小さな反復制限から開始して、その制限を繰り返し増加してより多くの結果を見つけるというものです。