Microsoft Japan Data Platform Tech Sales Team
SQL Server はこれまでもデータを格納するだけでなくデータの利活用を促進するために Integration Services、Analysis Services、Reporting Services といった機能を Enterprise Edition にて標準機能として提供してきました。 更に SQL Server 2016 ではデータの利活用を促進すべく Json 対応や R Services ( SQL Server 2017 では Machine Learning Services ) といった機能まで実装されました。そして次のバージョンである SQL Server 2017 では Graph Database の機能も実装されるとアナウンスされました。そこで今回は SQL Server に実装される Graph Database についてご紹介します。
SQL Server Graph Database ( 以降 Graph DB と称す ) とは Graph モデルを管理する機能となります。では Graph モデルとは何かですが、簡単に言うと点と線で表現されるデータ構造を持つものとなります。分かりやすい例でよく出てくるのが、人と人との関係や、機器と機器の関係、あるいは人と機器との関係といった情報などです。なお、グラフ構造には有向グラフと無向グラフがありますが、Graph DB は有向グラフ構造を取り扱います。また、Graph DB では上記でいう点をノード(Node)、関係をエッジ(Edge)、そしてそれぞれの属性値をプロパティ(Property) と呼びます。
図 1 Graph モデルの例 1
Graph DB のノードとエッジは SQL Server 内ではテーブルとして管理されます。
具体的に見ていった方が理解も早いと思いますので、図 1 の関係性を持つデータを以下で登録、そして参照してみましょう。
1. 準備
SQL Server 2017 を事前にインストールしていただくのはもちろんのこと、以下を実行するための SSMS もSSMS v17.0 RC3 をご利用ください。RC3 にてGraph DB 対応されており、Graph DB オブジェクトであるノードやエッジを作成すると、オブジェクトエクスプローラーにてテーブルの下にグラフテーブルというカテゴリがあり、その下にテーブルが追加されていきますので、その他の通常テーブルと区別がつきやすくなっています。
先ずはノードとエッジの入れ物 (テーブル) を作成します。
CREATE TABLE Person (ID INTEGER PRIMARY KEY, name VARCHAR(100),Age INTEGER) AS NODE; CREATE TABLE friends (StartDate date) AS EDGE;
作成されたノード ”Person”、 エッジ ”friends” に対応するテーブルを見てみると指定したプロパティ以外にも列が追加されています。
graph_id,from_obj_id,from_id,to_obj_id,to_id 列は内部で管理する内部グラフ列と呼ばれるものでユーザーが参照することはできません。
ここで理解しておく必要があるのはノード ”Person” の $node_id 列とエッジ “friends” の $from_id,$to_id 列です。
エッジの $from_id と $to_id には ノード の $node_id 列の値が入りますが、冒頭にて Graph DB は有向グラフを取り扱うとお伝えしたように、関係性を示すエッジには向きがあり、その向きを $from_id,$to_id で保持します。
2.データの登録
作成したノード ”Person” にデータを登録します。
INSERT INTO Person VALUES (1,'John',30),(2,'Mary',28),(3,'Alice',25);
データが登録されたか確認します。
次に、ノード間の関係をエッジ “friends” に登録します。
INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=1),(SELECT $node_id FROM Person WHERE ID=2),'2013/01/31'); INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=2),(SELECT $node_id FROM Person WHERE ID=3),'2010/05/05'); INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=3),(SELECT $node_id FROM Person WHERE ID=1),'2016/09/09');
同じくエッジ ”friends” にデータが登録されたか確認します。
これで図1に示す各人物とそれぞれの友達関係を登録することができました。
3.検索
ではここで”John”の友達を検索してみたいと思います。
SELECT Person2.Name FROM Person Person1, Friends, Person Person2 WHERE MATCH(Person1-(Friends)->Person2) AND Person1.Name = 'John';
Graph DB の検索には Where 句に Match を使用しますが、検索結果は以下となります。ここで、あれ?と思った方もいらっしゃるかもしれません。
図1を見ると ”John” は ”Mary” だけでなく ”Alice” とも友達のはずです。ただ、ここで図1の矢印に注意してください。”John” と ”Alice” の間の矢印は一方向にしか伸びていません。
そのため上記クエリは ”John” が友達と思っているのは?といったニュアンスの検索になっているために “Mary” しか返してこなかったのです。そこで ”John” を友達と思っている人も検索するためには以下のようにします。
SELECT Person2.Name FROM Person Person1, Friends, Person Person2 WHERE MATCH(Person1-(Friends)->Person2) AND Person1.Name = 'John' union all SELECT Person2.Name FROM Person Person1, Friends, Person Person2 WHERE MATCH(Person1<-(Friends)-Person2) AND Person1.Name = 'John';
すると “Mary” と “Alice” が結果として返ってきます。
ただ Twitter の Follow と違って、通常は友達関係というのは双方向のはずです。(中には私は友達と思っていたけど相手はそう思っていなかったなどという話もありますが、ここではそんな悲しい話はなしとします。)
そこで双方向に友達関係を作るために以下も追加します。
INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=2),(SELECT $node_id FROM Person WHERE ID=1),'2013/01/31'); INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=3),(SELECT $node_id FROM Person WHERE ID=2),'2010/05/05'); INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=1),(SELECT $node_id FROM Person WHERE ID=3),'2016/09/09');
すると、先ほどの最初のクエリを実行すると ”Mary” と ”Alice” が友達として返ってくることになります。
SELECT Person2.Name FROM Person Person1, Friends, Person Person2 WHERE MATCH(Person1-(Friends)->Person2) AND Person1.Name = 'John'
次に、もう少し範囲を広げて以下のような友達関係があったとします。
図 2 Graph モデルの例 2
この関係を登録するために追加で以下を実行します。
INSERT INTO Person VALUES (4,'Jacob',35),(5,'Julie',32); INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=3),(SELECT $node_id FROM Person WHERE ID=4),'2013/06/06'); INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=4),(SELECT $node_id FROM Person WHERE ID=5),'2012/02/02'); INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=4),(SELECT $node_id FROM Person WHERE ID=3),'2013/06/06'); INSERT INTO friends VALUES ((SELECT $node_id FROM Person WHERE ID=5),(SELECT $node_id FROM Person WHERE ID=4),'2012/02/02');
そしてこの状態で ”John” の友達の友達を検索してみます。
SELECT Person2.Name,Person3.Name FROM Person Person1, Friends Friends1, Person Person2, Friends Friends2, Person Person3 WHERE MATCH(Person1-(Friends1)->Person2-(Friends2)->Person3) AND Person1.Name = 'John' AND Person3.Name <> 'John'
※ ここで友達の友達として “John” も含まれる可能性があるために最後の行で友達の友達としての “John” を除いています。
“John” の友達は “Mary”,”Alice”ですが、その友達として “Alice”,”Mary”,”Jacob”が返ってきているのが分かります。
上記のように Graph DB は、これまでグラフ構造を RDBMS で検索するためには Join や再帰共通テーブル式(CTE)などを駆使して複雑なクエリになりがちであったものを簡単に管理できる特徴を持っていると共に、ノード、エッジはテーブルの機能を拡張して実装していることにより、SQL Server のこれまでの トランザクション管理や、バックアップ運用などにも統合されているという特徴も併せ持っています。まだ現時点(2017/4/26)ではCTP2.0であり、 Graph DB の威力を発揮する transitive closure や 最短経路検索は実装されておりませんが、こちらにもう少し複雑なサンプルなども載っておりますので是非お試しください。