増永教授のDB特論②「SQLの計算完備性」

 

1. はじめに

 SQL:1999 で再帰問合せ(recursive query)が導入されました.これにより SQL(本稿ではSQL:1999 及びそれ以降の SQL を総じて SQL ということにします)は SQL 自体で計算完備(computationally complete)あるいはチューリング完全(Turing complete)になったと言われています.しかしながら,それを証明してみなさいと言われると頭を抱えることになるかもしれません.そこで,本稿ではいくつかの文献を参照しながら,その証明の道筋を示してみたいと思います.
 ご存じのように,一般にプログラミング言語は計算完備だと言われています.なぜならば,プログラミング言語はあらゆるアルゴリズムを書き表すことが出来るからです.一方,SQL はリレーショナル代数演算をすべて書き表すことができるのでリレーショナル完備(relationally complete)と言われています.したがって,これら 2 つの性質を併せ持つ SQL はリレーショナル完備でかつ計算完備なデータベース言語となり,SQL の再帰問合せを駆使することによりあらゆるデータベースアプリケーション開発が行えるという枠組みが与えられたことになります.以下,SQL の計算完備性の証明とその意義を見ていきましょう.

2. SQL が計算完備であることを証明する道筋

 SQL が計算完備であることを証明する筋道は次の通りです.

(1)

計算可能性理論(computability theory)において,計算完備がどのように定義されているのかを見てみると,それは次の通りである:ある計算のメカニズムが万能チューリングマシン(universal Turing machine)と同じ計算能力を持つ,すなわち,いかなるチューリングマシンもシミュレートできるとき,それはチューリング完全あるいは計算完備であるという.ここに,チューリングマシン[1] とはイギリスの数学者 Turing が 1936 年に考案した計算モデルで,モデルの単純さにもかかわらず,アルゴリズムが与えられると,そのアルゴリズムのロジックをシミュレートできるチューリングマシンを構築できる.

(2)

あるチューリングマシンTについて,Tをシミュレートするタグシステム(tag system)[2] が必ず存在する.ここに,タグシステムとは 1943 年に Post が考案した計算モデルで,その後,Wang(1963年)と Cocke & Minsky(1964年)が 2-タグシステムで万能チューリングマシンのシミュレートが可能であることを示した.したがって,タグシステムはチューリング完全である.

(3)

タグシステムを巡回タグシステム(cyclic tag system)[2, 3] でシミュレートできる.したがって,巡回タグシステムはチューリング完全である.ちなみに,巡回タグシステムは 2004 年に Cook が考案した計算モデルである.

(4)

巡回タグシステムを再帰問合せを使って SQL でシミュレートできる[4].したがって,SQL はチューリング完全,すなわち計算完備である.

 これが,SQL が計算完備を示すための一つの道筋ですが,これをきちんと示そうとすれば,チューリングマシン,タグシステム,巡回タグシステムをそれぞれに理解し,さらにタグシステムがチューリングマシンをシミュレートできること,巡回タグシステムがタグシステムをシミュレートできることを理解したうえで,巡回タグシステムを SQL でシミュレートできることを示さなければなりません.しかしながら,そのためにはオートマトンと形式言語の理論のバックグラウンドが必須となります.そこで,本稿では上記(1)~(3)項には踏み込まず,(4)項の「巡回タグシステムを SQL でシミュレートできるが故に,SQL は計算完備である」に絞って議論することにしたいと思います.

 以下,巡回タグシステム,SQL の計算完備性,考察,と続きます.

3. 巡回タグシステム

 巡回タグシステム(cyclic tag system, CTS)[2] はタグシステムをシミュレートでき,したがって,計算完備であることが証明されるわけですが,ここではそこには深入りせず,まず,CTS とはどのようなシステムなのかを見ていきましょう.

【定義1】(CTS)

 CTS は 3 項組(A, P, I)である.ここに,A はアルファベット,P は生成規則群,I は初期単語を表す.

  • A – 記号 0 と 1 からなるアルファベット,すなわちA={0, 1}.A で構成される(空も含む)有限の文字列を単語(words)と呼ぶ.
  • P – 生成規則群.生成規則は単語の巡回リストになっている.
  • I – 初期単語を表す.

CTS の動作は次の通りである.

  • 動作 – 初期単語 I が与えられる.生成規則は単語の巡回リストになっているので,まず初期単語 I に巡回リストの先頭の単語から順次適用して初期単語を変換して新たな単語を生成していく.生成規則については,巡回リストの最後の単語を適用したら,次は先頭に戻る.アルファベットは 0 か 1 からなるので,変換の対象となる単語の左端の記号は 0 か 1 のいずれかである.もし左端の記号が 1 であれば,そのときの生成規則に示された単語を右端に追加し(この動作を「タグ付けする」という),0 であれば何も追加しない.どちらの場合も左端の記号を 1 つだけ削除する.

 理解を深めるために CTS の簡単な計算例を見てみましょう.

【CTSの計算例】

A={0, 1}

P=(1→11, 1→10, 1→ε)

 CTS では(上記のごとく)「0 であれば何も追加しない」ので,「1→」は自明と見なしてよいから,P=(1→11, 1→10, 1→ε)を単にP=(11, 10, ε)と書いてよい.なお,リスト P=(11, 10, ε)の要素は CTS の計算において,先頭から末尾の順に繰り返して適用されるので,それらに順に,1, 2, 3 と生成規則番号を付与することにする.
 そこで,初期単語 I をそれぞれ (a) 00001,(b) 10101,(c) 01100,(d) 11111 とした時の巡回タグシステムの動作を示す[3].なお,生成関係を⊦i (i∈{1, 2, 3})で表すこととする.このとき,単語の先頭が 0 の場合には生成後の単語はその先頭の 0 が削除されるだけなので,スペースを省略するために生成後の単語は書かないこととする.なお,CTS を提案した Cook はその停止条件を明示しなかったとのことであるが(タグシステムが停止するときに CTS も停止するように作られたということ),CTS の計算例を見てみると,その停止条件は,単語が消滅してしまうか(halt disappeared,消滅停止),ある所から単語の出現が周期的になってしまうか(halt periodic,周期的停止),のいずれかになるようである.

(a)

00001 ⊦12312 10 ⊦31 (消滅停止)

(b)

10101 ⊦1 010111 ⊦2312 1110 ⊦31 1011 ⊦2 01110 ⊦31 11011 ⊦2 101110 ⊦312 11010 ⊦31 01011 ⊦2312 110 ⊦31 011 ⊦231 11 ⊦2 110 ⊦31 011 (周期的停止)

(c)

01100 ⊦12 10010 ⊦31231 (消滅停止)

(d)

11111 ⊦1 111111 ⊦2 1111110 ⊦31 1111011 ⊦2 11101110 ⊦31 10111011 ⊦2 011101110 ⊦31 110111011 ⊦2 1011101110 ⊦312 1101110 ⊦31 0111011 ⊦231 101111 ⊦2 0111110 ⊦31 1111011 ⊦2 11101110 ⊦31 10111011 ⊦2 011101110 ⊦31 110111011 ⊦2 1011101110 ⊦312 110111010 ⊦31 011101011 ⊦231 10101111 ⊦2 010111110 ⊦31 0111110 ⊦231 111011 ⊦2 1101110 ⊦31 0111011 ⊦231 1011 ⊦2 01110 ⊦31 11011 ⊦2 101110 ⊦312 11010 ⊦31 01011 ⊦2312 110 ⊦31 011 ⊦231 11 ⊦2 110 ⊦31 011 (周期的停止)

4. SQLの計算完備性

 さて,巡回タグシステム(CTS)を SQL でシミュレートできれば,SQL はチューリング完全,即ち計算完備であることが示せたことになります.このために,SQL-92 で規格化された CASE 文,そして SQL:1999 で規格化された再帰問合せの機能を用います.以下に示すように,SQL 文で CTS をシミュレートできるので[4],SQL がチューリング完全であることが示されます.

【CTS をシミュレートする再帰問合せ】

 CTS=(A, P, I),ここに A={0, 1}, P=(110, 01, 0000), I=1 としてそれをシミュレートする SQL の再帰問合せと実行結果を以下に示す(PostgreSQL 14 で実行).

(1)

CTS の生成規則群 P をテーブル p と符号化する.ここに,「iter」は生成規則番号,「rnum」はビットのインデックス,「tag」はビット値を表す.ちなみに,CTS の生成規則リストを P=(110, 01, 0000) とすれば p(iter, rnum, tag)={(0,0,1), (0,1,1), (0,2,0), (1,0,0), (1,1,1), (2,0,0), (2,1,0), (2,2,0), (2,3,0)} であるが,(0,0,1), (0,1,1), (0,2,0) で第 1 番目の生成規則 110 を,(1,0,0), (1,1,1) で第 2 番目の生成規則 01 を,(2,0,0), (2,1,0), (2,2,0), (2,3,0 )で第 3 番目の生成規則 0000 を表している.mod 関数が用いられているので,生成規則には順に 0, 1, 2 と順番がつけられている(モードは 3).

(2)

CTS の初期単語 I を非再帰 UNION 表現(non-recursive union term)に符号化する,この例では CTS で I=1 なので(0, 0, 1)とする.

(3)

mod(r.iter, n) 部分式は,生成規則の数を符号化している.これは、空の生成規則(1→ε)がテーブル p に含まれていないため,テーブル p のサイズよりも大きくなる可能性がある.この例では(上述のように)「n=3」である.

(4)

CTS の動作で規定したように,各反復で,単語の先頭ビット 0 が削除され,残りのビットが 1 つ上にシフトされる.先頭ビットが 1 の場合にのみ,(先頭ビット 1 が削除され)その時の生成規則の内容が単語の最後に追加される.

■ CTS をシミュレートする SQL の再帰問合せ[4]
 

postgres=# WITH RECURSIVE
postgres-# p(iter, rnum, tag) AS (
postgres(#   VALUES(0,0,1),(0,1,1),(0,2,0),(1,0,0),(1,1,1),(2,0,0),(2,1,0),(2,2,0),(2,3,0)
postgres(# ),
postgres-# r(iter,rnum,tag) AS (
postgres(#   VALUES (0,0,1)
postgres(# UNION ALL
postgres(#   SELECT r.iter+1,
postgres(#     CASE
postgres(#      WHEN r.rnum=0 THEN p.rnum + max(r.rnum) OVER ()
postgres(#      ELSE r.rnum-1
postgres(#     END,
postgres(#     CASE
postgres(#      WHEN r.rnum=0 THEN p.tag
postgres(#      ELSE r.tag
postgres(#     END
postgres(#   FROM
postgres(#    r
postgres(#   LEFT JOIN p
postgres(#    ON (r.rnum=0 and r.tag=1 and p.iter=mod(r.iter, 3))
postgres(#   WHERE
postgres(#    r.rnum>0
postgres(#   OR p.iter IS NOT NULL
postgres(# )
postgres-# SELECT iter, rnum, tag
postgres-# FROM r
postgres-# ORDER BY iter, rnum;

■ PostgreSQL14 での実行結果

PostgreSQL 14 exec

ちなみに,この SQL 文がシミュレートした CTS の動作は次のとおりである.

1 ⊦1 110 ⊦2 1001 ⊦3 0010000 ⊦12 10000 ⊦3 00000000 ⊦12312312 ε

このように CTS を作動させると 14 ステップで消滅停止することになるが,それを上記 SQL 文でシミュレートすると 62 行からなるテーブル r として結果を出力している.このとき,r の最終行は(13, 0, 0)となっており,CTS の 14 ステップでの消滅停止と符合している.

なお,CTS の動作を Python で 114 文字(スペースを含める)で書けるという報告があります[5].

while len(word) > S : pIndex, word = (pIndex + 1) % len(C), word[1:] + C[pIndex] if (word0 == “1”) else word[1:]

そもそもプログラミング言語自体は計算完備になるように設計されているから当り前と言えば当たり前ですし,逆に Python で CTS をシミュレートできるから Python は計算完備だと主張しているのでしょうか.あまり深い意味はないと思いますが,他のプログラミング言語でも同様な報告が可能でしょう.

5. 考察

 以上,SQL が計算完備であることを示すことができました.したがって,SQL はリレーショナル完備でかつ計算完備という強力なプログラミング言語であると主張することにやぶさかではないといえます.しかし,だったら,あらゆるデータベースアプリケーションプログラムを SQL の再帰問合せを駆使して書き上げるのか?と問われればそれは別の話になるのではないでしょうか.
 この件に関して,SQL に精通し長年にわたり ISO/IEC JTC1/SC 32 WG 3 (SQL)日本代表を務められた小寺孝氏(日立製作所)に意見を求めたことがあります.氏は,次のような認識を示して下さいました(小寺氏との私信[6]による).

『まず,再帰問合せによって計算完備性を実現できると思いますが再帰問合せは計算完備性のための機能ではないと思います.現状,再帰問合せの向きは部品展開や経路探索のような問題にあると思います.個人的には再帰問合せはもっと可能性があるし,もっと用途が拡大して欲しいと思っていますが,通常のプログラム言語のように手法が開発されないと難しいと思います.ただ,複雑な再帰問合せを記述しても問合せの意図を解釈してそれに即した最適化を行うことが SQL では必要になるという問題があります.DBMS が再帰問合せの意図を解読するのは殆ど困難だと思います(ただし,今後機械学習を使うようなことが始まるとどうなるかは今の所察しがつきません).なので,再帰問合せで記述ができる問題でも SQL/OLAP とか用途に即した機能を結局は作ることになりました.再帰問合せによって計算完備が実現できること自体は正しいと思いますがそのような使い方が想定されているわけではないので,少なくとも標準 SQL では再帰問合せが計算完備とは謳ってはいません.』

 筆者も小寺氏の認識に大いに賛同するところです.SQL-87 当時から導入された「埋込みSQL」(embedded SQL)や SQL-92 を拡張する追加規格SQL/PSM (ISO/IEC 9075-4:1996)は,正しく SQL の計算完備性をアプリケーション開発の観点から補う具体的な機能であって,アプリケーションプログラマは「再帰問合せ」[7],「埋込み SQL」,「SQL/PSM」の持ち合わせる機能をよく理解して,的確なプログラミングに心がけることが肝要ということだと思います.

【謝辞】

 SQL の計算完備性とデータベースアプリケーション開発について正鵠を射た認識を披露して下さり,またそれを本稿に転載することを承諾して下さった小寺孝氏(日立製作所)に深謝いたします.

【文献】

[1]

Turing machine. https://en.wikipedia.org/wiki/Turing_machine

[2]

Tag System. https://en.wikipedia.org/wiki/Tag_system

[3]

Genaro J. Martınez, Harold V. McIntosh, Juan C. Seck Tuoh Mora, and Sergio V. Chapa Vergara. Reproducing the cyclic tag system developed by Matthew Cook with Rule 110 using the phases fi_1. https://core.ac.uk/download/pdf/323897802.pdf

[4]

Cyclic Tag System. 2015/02/20. https://wiki.postgresql.org/wiki/Cyclic_Tag_System

[5]

Bar Vinograd. Cyclic Tag System: 1 Line of Turing-Complete Code. Feb 25, 2018. https://medium.com/@barvinograd1/cyclic-tag-system-1-line-of-turing-complete-code-cebe8e18658f

[6]

小寺孝氏との私信(personal communication).April 2, 2021

[7]

土田正士,小寺孝.SQL2003 ハンドブック,4.4 節 再帰問合せ.ソフト・リサーチ・センター,2004.