読者です 読者をやめる 読者になる 読者になる
無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

共通表式を使った再帰SQL

SQL

DB2の共通表式中のFROM節では共通表自体を指定することができ、このとき共通表式は再帰共通表式となります。再帰共通表式を使うことで、階層構造を持つテーブルデータを再帰的に探索したりできます。

例えば、以下のようなファイルの関連があるとして、

これの関連情報を↓のようなテーブルで保持するとします。

create table file_relation ( 
  from VARCHAR(20) NOT NULL, 
  to   VARCHAR(20) NOT NULL, 
  PRIMARY KEY(from,to) 
) in foo
$ db2 "select * from file_relation" 

FROM                 TO                  
-------------------- --------------------
a                    b                   
a                    c                   
a                    d                   
c                    e                   
c                    f                   
d                    g                   
x                    y                   
x                    z                   

  8 record(s) selected.

ここで、以下の再帰共通表式を使うと「"a"配下のファイル一覧を再帰的に取り出す」ことができます。

WITH X(from,to) AS ( 
  SELECT from, to FROM file_relation WHERE from = 'a' 
  UNION ALL 
  SELECT file_relation.from, file_relation.to FROM X, file_relation WHERE file_relation.from = X.to  
) SELECT DISTINCT to FROM X"

実行結果です。「このSQLだと無限ループになる場合があるよ」という内容の警告が表示されていますが、a配下のファイルをちゃんと取得できています。

TO                  
--------------------
SQL0347W  The recursive common table expression "XFYADMIN.X" may contain an 
infinite loop.  SQLSTATE=01605

b                   
c                   
d                   
e                   
f                   
g                   

  6 record(s) selected with 1 warning messages printed.

c,x,bの配下もちゃんととれます。

$ db2 "WITH X(from,to) AS ( SELECT from, to FROM file_relation  WHERE from = 'x' UNION ALL SELECT file_relation.from, file_relation.to FROM X, file_relation WHERE file_relation.from = X.to  ) SELECT DISTINCT to FROM X"

TO                  
--------------------
y                   
z                   

  2 record(s) selected with 1 warning messages printed.

$ db2 "WITH X(from,to) AS ( SELECT from, to FROM file_relation  WHERE from = 'c' UNION ALL SELECT file_relation.from, file_relation.to FROM X, file_relation WHERE file_relation.from = X.to  ) SELECT DISTINCT to FROM X"

TO                  
--------------------
e                   
f                   

  2 record(s) selected with 1 warning messages printed.

$ db2 "WITH X(from,to) AS ( SELECT from, to FROM file_relation  WHERE from = 'b' UNION ALL SELECT file_relation.from, file_relation.to FROM X, file_relation WHERE file_relation.from = X.to  ) SELECT DISTINCT to FROM X"

TO                  
--------------------

  0 record(s) selected with 1 warning messages printed.

内部的な動作は@IT - オブジェクト指向、Javaを取り入れた新しい業界標準「SQL99」詳細解説 が詳しいのですが、

  • 1.共通表式内の「SELECT from, to FROM file_relation WHERE from = 'a' 」で「'a'と関連づけられているファイル一覧」を取り出して、X表に追加する。
  • 2.さらに次に続く「SELECT file_relation.from, file_relation.to FROM X, file_relation WHERE file_relation.from = X.to」で、1で取り出したファイル一覧に関連づけられているファイル一覧を取り出し、X表に追加する。
  • 3.1〜2を子がなくなるまで繰り返す。
  • 4.最後に、X表から関連先ファイル名を取り出す。

となるらしい。ふむふむ。

循環するとどうなるか。

↑のSQLでは循環をチェックしていないので、データを以下のような構造にすると、さくっと無限ループします。


$ db2 "select * from file_relation" 

FROM                 TO                  
-------------------- --------------------
a                    b                   
a                    c                   
a                    d                   
c                    e                   
c                    f                   
d                    g                   
f                    a                   
x                    y                   
x                    z                   

  9 record(s) selected.
$ db2 "WITH X(from,to) AS ( SELECT from, to FROM file_relation  WHERE from = 'a' UNION ALL SELECT file_relation.from, file_relation.to FROM X, file_relation WHERE file_relation.from = X.to  ) SELECT DISTINCT to FROM X"
・・・帰ってこない・・・

回避策を30分くらい考えたけどさっぱり思いつきません。無念..。一般的にはどうするんだろー。「最大XX階層まで辿る」とか? > 今度ググる