5.8 SQLグラフ問合せの例

この項では、固定長および可変長のグラフ・パターン一致問合せを使用したSQLプロパティ・グラフの問合せの例をいくつか示します。

例に示されているすべての問合せは、例4-1で作成されたSQLプロパティ・グラフstudents_graphで実行されます:

例5-5 左から右へのエッジ・パターンを使用した問合せ

次の例では、左から右へのエッジ・パターン(-[e IS friends]->)を含むGRAPH_TABLE問合せを示します:

SELECT * FROM GRAPH_TABLE (students_graph
  MATCH
  (a IS person) -[e IS friends]-> (b IS person WHERE b.name='Alice')
  WHERE a.name='Mary'
  COLUMNS (a.name AS person_a, b.name AS person_b)
);

このコードによって、次の出力が生成されます。

PERSON_A   PERSON_B
---------- ----------
Mary       Alice

例5-6 右から左へのエッジ・パターンを使用した問合せ

次の例では、右から左へのエッジ・パターン(<-[e IS friends]-)を含む問合せを示します。

SELECT * FROM GRAPH_TABLE (students_graph
  MATCH
  (a IS person) <-[e IS friends]- (b IS person WHERE b.name='Mary')
  WHERE a.name='Alice'
  COLUMNS (a.name AS person_a, b.name AS person_b)
);

このコードによって、次の出力が生成されます。

PERSON_A   PERSON_B
---------- ----------
Alice      Mary

例5-7 任意の方向のエッジ・パターンを使用した問合せ

次の例では、任意の方向のエッジ・パターン(-[e IS friends]-)を含む問合せを示します。

SELECT * FROM GRAPH_TABLE (students_graph
  MATCH
  (a IS person) -[e IS friends] - (b IS person WHERE b.name='Alice' OR b.name='Mary')
  WHERE (a.name='Alice' OR a.name='Mary')
  COLUMNS (a.name AS person_a, b.name AS person_b)
); 

このコードによって、次の出力が生成されます。

PERSON_A   PERSON_B
---------- ----------
Mary       Alice
Alice      Mary

例5-8 匿名エッジ変数を使用した問合せ

次の例では、エッジ要素変数が省略されている問合せを示します。

SELECT * FROM GRAPH_TABLE (students_graph
  MATCH
  (a IS person) -[]-> (b IS person)
  COLUMNS (a.name AS person_a, b.name AS person_b)
  );

または、前述の問合せのエッジ・パターン(-[]->)のカッコで囲まれた構文を、省略した構文->に置き換えることができます。

このコードによって、次の出力が生成されます。

PERSON_A   PERSON_B
---------- ----------
Mary       John
Bob        Mary
John       Bob
Mary       Alice

例5-9 複数のパス・パターンを使用した問合せ

次の例では、共通の頂点を持つ2つのパス・パターン(a)->(b), (a)->(c))を含む問合せを示します。

SELECT * FROM GRAPH_TABLE (students_graph
  MATCH
  (a IS person WHERE a.name = 'John') -> (b IS person),(a IS person WHERE a.name = 'John') -> (c IS university)
  COLUMNS (a.name AS person_a, b.name AS person_b,c.name as university)
  );

このコードによって生成される出力は、次のとおりです。

PERSON_A   PERSON_B   UNIVERSITY
---------- ---------- ----------
John       Bob        ABC

例5-10 非結合パス・パターンを使用した問合せ

次の例では、2つの非結合パス・パターンを含む問合せを示します。

SELECT * FROM GRAPH_TABLE (students_graph
MATCH (a IS person WHERE a.name='John') -[IS student_of]-> (b IS university),
(x IS person) -[IS friends]-> (y IS person)
COLUMNS (a.name AS a, b.name as university, x.name AS x, y.name as y)
);

結果の出力は次のようになります。

A          UNIVERSITY X          Y
---------- ---------- ---------- ----------
John       ABC        Mary       John
John       ABC        Bob        Mary
John       ABC        John       Bob
John       ABC        Mary       Alice

例5-11 循環パス・パターンを使用した問合せ

次の例では、循環パス・パターン(MATCH (a)-[]->(b)-[]->(c)-[]->(a))を使用します。この例では、同じ頂点パターン変数名a (personにバインドされているもの)が2回使用されています。そのため、最終的にa自体にバインドされる3つのエッジを含むグラフの循環が検索されます。

SELECT * FROM GRAPH_TABLE (students_graph
  MATCH
  (a IS person) -[IS friends]-> (b IS person) -[IS friends]->
  (c IS person) -[IS friends]-> (a)
  COLUMNS (a.name AS person_a, b.name AS person_b, c.name AS person_c)
  );

このコードによって生成される出力は、次のとおりです。

PERSON_A   PERSON_B   PERSON_C
---------- ---------- ----------
Bob        Mary       John
John       Bob        Mary
Mary       John       Bob

例5-12 ラベルの論理和を使用した問合せ

次の例では、頂点ラベル式でラベルの論理和を使用します。

SELECT * FROM GRAPH_TABLE (students_graph
MATCH
(a is person|university)
COLUMNS (a.name, a.dob)
);

このコードによって、次の出力が生成されます。

NAME       DOB
---------- ---------
John       13-JUN-63
Mary       25-SEP-82
Bob        11-MAR-66
Alice      01-FEB-87
ABC        NULL
XYZ        NULL

6 rows selected.

例5-13 ラベルの論理積を使用した問合せ

次の例では、頂点ラベル式でラベルの論理積を使用します。

SELECT * FROM GRAPH_TABLE (students_graph
MATCH
(a IS person), (a IS person_ht)
COLUMNS (a.name as name, a.dob as dob, a.height as height )
);

このコードによって、次の出力が生成されます。

NAME       DOB           HEIGHT
---------- --------- ----------
John       13-JUN-63        1.8
Mary       25-SEP-82       1.65
Bob        11-MAR-66       1.75
Alice      01-FEB-87        1.7

例5-14 制限数量詞付きの再帰パス・パターンを使用した問合せ

次の例では、再帰パス・パターンを使用して、2ホップ内のすべての友人を取得します:

SELECT * FROM GRAPH_TABLE (students_graph
MATCH (a is person WHERE a.name='Mary') -[is friends]->{2} (b is person)
COLUMNS (a.name AS a , b.name AS b)
);

このコードによって生成される出力は、次のとおりです。

A          B
---------- ----------
Mary       Bob

次の例では、再帰パス・パターンを使用して、1から2ホップ内(両端を含む)のすべての友人を取得します:

SELECT * FROM GRAPH_TABLE (students_graph
MATCH (a is person WHERE a.name='Mary') -[is friends]->{1, 2} (b is person)
COLUMNS (a.name AS a , b.name AS b)
);

このコードによって生成される出力は、次のとおりです。

A          B
---------- ----------
Mary       Alice
Mary       John
Mary       Bob

次の例では、再帰パス・パターンを使用して、0から2の反復を実行することですべての友人を取得します:

SELECT * FROM GRAPH_TABLE (students_graph
MATCH (a is person WHERE a.name='Mary') -[is friends]->{,2} (b is person)
COLUMNS (a.name AS a , b.name AS b)
);

このコードによって生成される出力は、次のとおりです。

A          B
---------- ----------
Mary       Mary
Mary       Alice
Mary       John
Mary       Bob

前述の出力の最初の行では、Maryは要素パターン変数abの両方にバインドされています。これは、問合せにゼロ・ホップ反復が含まれているため、左側の頂点パターンと右側の頂点パターンが同じグラフ要素にバインドする必要があるためです。

例5-15 集計を使用した問合せ

次の例では、Johnという名前の個人から開始し、friendsとラベル付けされた出力エッジとpersonとラベル付された頂点のみをたどり、長さがエッジ2つから3つまで({2,3})であるすべてのパスを検索します。パス上の頂点のperson_idJohnのものとは異なっている必要があります(WHERE p.person_id <> friend.person_id)。この例では、COLUMNS句で次の4つの集計を使用します。

  • LISTAGG: 1つ目ではパスに沿って個人名のカンマ区切りリストを作成し、2つ目ではパスに沿って個人の年齢のカンマ区切りリストを作成します。
  • AVG: これにより、パス内の個人グループの平均年齢を計算します。
  • COUNT: これにより、各パスの長さを計算します。
SQL> SELECT * FROM GRAPH_TABLE ( students_graph
MATCH (p IS person) (-[e IS friends]-> (friend IS person)
                      WHERE p.person_id <> friend.person_id){2,3}
WHERE p.name = 'John'
COLUMNS (LISTAGG(friend.name, ',') as fnames,
         LISTAGG(EXTRACT(YEAR from SYSDATE) - EXTRACT(YEAR from friend.dob), ',') AS age_list,
         AVG(EXTRACT(YEAR from SYSDATE) - EXTRACT(YEAR from friend.dob)) AS avg_age_group,
         COUNT(e.friendship_id) AS path)); 

このコードによって生成される出力は、次のとおりです。

FNAMES                         AGE_LIST        AVG_AGE_GROUP       PATH
------------------------------ --------------- ------------- ----------
Bob,Mary                       57,41                   49.00          2
Bob,Mary,Alice                 57,41,36                44.67          3

次の例では、パスの長さはエッジ3つまで({,3})で、university ABCとuniversity XYZの間のすべてのパスを検索します。パスごとにJSON配列が戻されます。この配列には、friendsとラベル付けされたエッジのfriendship_id値、およびstudent_ofとラベル付けされたエッジのsubject値が含まれています。なお、friendship_idプロパティはVARCHAR(100)にキャストされ、subjectプロパティと型互換になります。

SELECT * FROM GRAPH_TABLE ( students_graph
MATCH (u1 IS university) -[e]-{,3} (u2 IS university)
WHERE u1.name = 'ABC' AND u2.name = 'XYZ'
COLUMNS (JSON_ARRAYAGG(CASE WHEN e.subject IS NOT NULL THEN e.subject                         
                       ELSE CAST(e.friendship_id AS VARCHAR(100)) END) AS path));

このコードによって生成される出力は、次のとおりです。

PATH
-----------------------------------------
["Arts","3","Math"]
["Music","4","Math"]

例5-16 バインド変数を使用した問合せ

この例では、バインド変数nameを宣言し、次のように値を割り当てます:

SQL> variable name VARCHAR2(10);
SQL> BEGIN
  2  :name := 'Bob';
  3  END;
  4  /

PL/SQL procedure successfully completed.

このバインド変数を使用して、次の問合せが実行されます。

SELECT * FROM GRAPH_TABLE (students_graph
  MATCH
  (a IS person) -[e IS friends]-> (b IS person WHERE b.name=:name)
  WHERE a.name='John'
  COLUMNS (a.name AS person_a,
           b.name AS person_b,
           e.meeting_date AS met_on)
);

このコードによって、次の出力が生成されます。

A          B          MET_ON
---------- ---------- ---------
John       Bob        01-SEP-00

例5-17 式内のCOLUMNS句でPL/SQL関数を呼び出す問合せ

この例では、ユーザー定義関数(UDF)を次のように宣言します:

CREATE OR REPLACE FUNCTION get_age(
    id NUMBER
)
RETURN NUMBER
AS
    age NUMBER := 0;
BEGIN
    -- get age
      SELECT (EXTRACT(YEAR from SYSDATE) - EXTRACT(YEAR from birthdate))
      INTO age 
      FROM persons
      WHERE person_id=id;
    -- return age
    RETURN age;
END;
/

Function created.

次の問合せは、WHERE句の式内でUDFを呼び出し、COLUMNS句内で再度呼び出します。

SELECT * FROM GRAPH_TABLE (students_graph
  MATCH
   (a IS person) -[e IS friends]-> (b IS person)
  WHERE (get_age(a.person_id) > 50)
  COLUMNS (a.name AS a,
        get_age(a.person_id) AS age,
           b.name AS b,
           e.meeting_date AS met_on)
  );

このコードによって、次の出力が生成されます。

A                 AGE B          MET_ON
---------- ---------- ---------- ---------
John               60 Bob        01-SEP-00
Bob                57 Mary       10-JUL-01

例5-18 SCNを使用した問合せ

次のように、データベースの現在のSCN値を確認します:

SQL>  SELECT TIMESTAMP_TO_SCN(SYSDATE) FROM DUAL;

TIMESTAMP_TO_SCN(SYSDATE)
-------------------------
                  2117789

次の問合せでは、前述のSCN値を使用します。

SELECT * FROM GRAPH_TABLE (students_graph AS OF SCN 2117789
  MATCH
   (a IS person) -[e]-> (b IS person)
  COLUMNS (a.name AS a, b.name AS b, e.meeting_date AS met_on)
  );

問合せの出力は次のようになります。

A          B          MET_ON
---------- ---------- ---------
Mary       John       19-SEP-00
Bob        Mary       10-JUL-01
John       Bob        01-SEP-00
Mary       Alice      19-SEP-00

例5-19 TIMESTAMPを使用した問合せ

次の問合せでは、次のようにTIMESTAMP値を使用します。

SQL> SELECT * FROM GRAPH_TABLE (students_graph AS OF TIMESTAMP SYSTIMESTAMP
  MATCH
   (a IS person WHERE a.name='John') -[e]-> (b IS person)
  COLUMNS (a.name AS a, b.name AS b, e.meeting_date AS met_on)
  );

問合せの出力は次のようになります。

A          B          MET_ON
---------- ---------- ---------
John       Bob        01-SEP-00

例5-20 VERTEX_ID識別子とEDGE_ID識別子を使用した問合せ

SELECT * FROM GRAPH_TABLE (students_graph
MATCH
 (a IS person ) -[e IS friends]-> (b IS person)
COLUMNS (JSON_SERIALIZE(VERTEX_ID(a)) AS id_a , JSON_SERIALIZE(EDGE_ID(e)) AS id_e)
);

この問合せでは、グラフ所有者、グラフ名、グラフ要素の表名およびキー値を含むJSONデータ型の出力が生成されます:

ID_A                        ID_E
--------------------------- ------------------------------
{"GRAPH_OWNER":"GRAPHUSER", {"GRAPH_OWNER":"GRAPHUSER","GR
"GRAPH_NAME":"STUDENTS_GRAP APH_NAME":"STUDENTS_GRAPH","EL
H","ELEM_TABLE":"PERSONS"," EM_TABLE":"FRIENDS","KEY_VALUE
KEY_VALUE":{"PERSON_ID":1}} ":{"FRIENDSHIP_ID":1}}

{"GRAPH_OWNER":"GRAPHUSER", {"GRAPH_OWNER":"GRAPHUSER","GR
"GRAPH_NAME":"STUDENTS_GRAP APH_NAME":"STUDENTS_GRAPH","EL
H","ELEM_TABLE":"PERSONS"," EM_TABLE":"FRIENDS","KEY_VALUE
KEY_VALUE":{"PERSON_ID":2}} ":{"FRIENDSHIP_ID":2}}

{"GRAPH_OWNER":"GRAPHUSER", {"GRAPH_OWNER":"GRAPHUSER","GR
"GRAPH_NAME":"STUDENTS_GRAP APH_NAME":"STUDENTS_GRAPH","EL
H","ELEM_TABLE":"PERSONS"," EM_TABLE":"FRIENDS","KEY_VALUE
KEY_VALUE":{"PERSON_ID":2}} ":{"FRIENDSHIP_ID":3}}

{"GRAPH_OWNER":"GRAPHUSER", {"GRAPH_OWNER":"GRAPHUSER","GR
"GRAPH_NAME":"STUDENTS_GRAP APH_NAME":"STUDENTS_GRAPH","EL
H","ELEM_TABLE":"PERSONS"," EM_TABLE":"FRIENDS","KEY_VALUE
KEY_VALUE":{"PERSON_ID":3}} ":{"FRIENDSHIP_ID":4}}

例5-21 VERTEX_EQUAL述語を使用した問合せ

SELECT * FROM GRAPH_TABLE (students_graph
MATCH
 (a IS person WHERE a.name='John') -[e IS friends]->{,1} (b IS person)
WHERE VERTEX_EQUAL(a,b)
COLUMNS (JSON_SERIALIZE(VERTEX_ID(a)) AS id_a , JSON_SERIALIZE(VERTEX_ID(b)) AS id_b)
);

この問合せでは、グラフ所有者、グラフ名、グラフ要素の表名およびキー値を含むJSONデータ型の出力が生成されます:

ID_A                        ID_B
--------------------------- ---------------------------
{"GRAPH_OWNER":"GRAPHUSER", {"GRAPH_OWNER":"GRAPHUSER",
"GRAPH_NAME":"STUDENTS_GRAP "GRAPH_NAME":"STUDENTS_GRAP
H","ELEM_TABLE":"PERSONS"," H","ELEM_TABLE":"PERSONS","
KEY_VALUE":{"PERSON_ID":1}} KEY_VALUE":{"PERSON_ID":1}}

5.8.1 データベースでのサンプル・データの設定

「SQLプロパティ・グラフの作成」に示されているSQLプロパティ・グラフstudents_graphを作成するには、データベースに次のサンプル表とデータを設定する必要があります。
  1. スキーマ・ユーザーとしてデータベースに接続します。
  2. 次のSQLスクリプトを実行して、データベースにサンプル・データを含むuniversitypersonsstudentsおよびfriendships表を作成します。
    CREATE TABLE university (
        id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
        name VARCHAR2(10),
        CONSTRAINT u_pk PRIMARY KEY (id));
    
    INSERT INTO university (name) VALUES ('ABC');
    INSERT INTO university (name) VALUES ('XYZ');
    
    CREATE TABLE persons (
         person_id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT
         BY 1),
         name VARCHAR2(10),
         birthdate DATE,
         height FLOAT DEFAULT ON NULL 0,
         hr_data JSON,
         CONSTRAINT person_pk PRIMARY KEY (person_id)
       );
    
    INSERT INTO persons (name, height, birthdate, hr_data)
           VALUES ('John', 1.80, to_date('13/06/1963', 'DD/MM/YYYY'), '{"department":"IT","role":"Software Developer"}');
    
    INSERT INTO persons (name, height, birthdate, hr_data)
           VALUES ('Mary', 1.65, to_date('25/09/1982', 'DD/MM/YYYY'), '{"department":"HR","role":"HR Manager"}');
    
    INSERT INTO persons (name, height, birthdate, hr_data)
           VALUES ('Bob', 1.75, to_date('11/03/1966', 'DD/MM/YYYY'), '{"department":"IT","role":"Technical Consultant"}');
    
    INSERT INTO persons (name, height, birthdate, hr_data)
           VALUES ('Alice', 1.70, to_date('01/02/1987', 'DD/MM/YYYY'), '{"department":"HR","role":"HR Assistant"}');
    
    CREATE TABLE student_of (
          s_id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
          s_univ_id NUMBER,
          s_person_id NUMBER,
          subject VARCHAR2(10),
          CONSTRAINT stud_pk PRIMARY KEY (s_id),
          CONSTRAINT stud_fk_person FOREIGN KEY (s_person_id) REFERENCES persons(person_id),
          CONSTRAINT stud_fk_univ FOREIGN KEY (s_univ_id) REFERENCES university(id)
        );
    
    INSERT INTO student_of(s_univ_id, s_person_id,subject) VALUES (1,1,'Arts');
    INSERT INTO student_of(s_univ_id, s_person_id,subject) VALUES (1,3,'Music');
    INSERT INTO student_of(s_univ_id, s_person_id,subject) VALUES (2,2,'Math');
    INSERT INTO student_of(s_univ_id, s_person_id,subject) VALUES (2,4,'Science');
    
    CREATE TABLE friends (
        friendship_id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
        person_a NUMBER,
        person_b NUMBER,
        meeting_date DATE,
        CONSTRAINT fk_person_a_id FOREIGN KEY (person_a) REFERENCES persons(person_id),
        CONSTRAINT fk_person_b_id FOREIGN KEY (person_b) REFERENCES persons(person_id),
        CONSTRAINT fs_pk PRIMARY KEY (friendship_id)
    );
    
    INSERT INTO friends (person_a, person_b, meeting_date) VALUES (1, 3, to_date('01/09/2000', 'DD/MM/YYYY'));
    INSERT INTO friends (person_a, person_b, meeting_date) VALUES (2, 4, to_date('19/09/2000', 'DD/MM/YYYY'));
    INSERT INTO friends (person_a, person_b, meeting_date) VALUES (2, 1, to_date('19/09/2000', 'DD/MM/YYYY'));
    INSERT INTO friends (person_a, person_b, meeting_date) VALUES (3, 2, to_date('10/07/2001', 'DD/MM/YYYY'));