Tutorial DとErqの比較

Tutorial DとErqは部分的に似た文法を持っているが、その目的は異なっている。Tutorial Dは、その目的が数学的により純粋な関係代数を実現することであるのに対し、ErqはSQLのセマンティクスを保ったまま文法を異なるものにしている。

この記事では、Tutorial DとErqを簡単に比べる。なお、この記事でTutorial Dとして例示するもは、Project:M36 Relational Algebra Engineが実装している記法である。

(Project:M36を実際に動かして試したかったが、手元の環境でビルドに失敗して試せていない。)

セマンティクスの違い

純粋な関係か、多重関係か

Tutorial Dは関係代数に忠実なセマンティクスを持っている。そこで扱われる関係はタプルの集合であって、重複したタプルを持たないし、属性の順番は重要ではないし、関係に含まれるタプルの間に暗黙の順序はない。

一方でErqのセマンティクスは基本的にはSQLと同じであって、扱われるテーブルは純粋な関係とは限らず、重複したレコードを許すし、カラムの順番は重要で、暗黙の順序を持っている。

NULL・三値論理を採用するか

SQLはTRUE/FALSE/UNKNOWNの三値論理を採用している。(SQLiteの場合、UNKNOWNの代わりにNULLを使う。)そのせいで、値にNULLが絡んできた場合の対応が面倒なことになっている。

演算の比較

Tutorial DとErqで、個々の演算を比較してみる。データとして、TutorialD Tutorial for Project:M36で使われているものと同じ、Chris Dateのサンプル関係データを用いる。

関係(テーブル)s (suppliers) の内容

s#,sname,status,city
S3,Blake,30,Paris
S4,Clark,20,London
S5,Adams,30,Athens
S1,Smith,20,London
S2,Jones,10,Paris

関係(テーブル)p (products) の内容

p#,pname,color,weight,city
P6,Cog,Red,19,London
P5,Cam,Blue,12,Paris
P1,Nut,Red,12,London
P4,Screw,Red,14,London
P3,Screw,Blue,17,Oslo
P2,Bolt,Green,17,Paris

関係(テーブル) sp (supplierProducts) の内容

s#,p#,qty
S1,P1,300
S1,P2,200
S1,P3,400
S1,P4,200
S1,P5,100
S1,P6,100
S2,P1,300
S2,P2,400
S3,P2,200
S4,P2,200
S4,P4,300
S4,P5,400

Erq/SQLでは値にNULLが入る場合があるが、それを考慮すると複雑になってしまうので、ここではNULLが入らない場合だけについて考えることにする。

関係自体の表示

Tutorial DもErqも、関係それ自体を表すのに余計なキーワードを必要としない。関係(テーブル) p を表示したいのであれば、単に p をクエリすればよい。

属性の改名

Tutorial Dの場合は属性(カラム)の改名のシンタックスが存在して、

s rename {city as town}

のようにすると、属性cityをtownに改名できる。

Erqは、少なくとも現状ではカラムの改名の記法は存在しないので、ブレース記法で残りのカラムを選択する必要がある。

s{`s#`,sname,status,town: city}

射影

Tutorial DとErqで、射影の記法は似ている。どちらもブレースを使って、属性を選択することができる。

p{color,city}

しかしTutorial Dは関係代数に忠実であるのに対し、ErqはSQLと同じセマンティクスを持っている。すなわち、Tutorial Dの場合はタプルの重複は除去されるので、この結果は4件になる一方で、Erqの場合は重複が除去されず、結果は6件になる。

Erqで重複タプルを除去するには、明示的にdistinctをつける必要がある。

p{color,city} distinct

結合

Tutorial Dではjoinは自然結合のこと。

s join sp

ErqではSQL同様natural joinを使う。

s natural join sp

射影の略記

s join sp から関係 s に含まれる属性すべての射影をとるとき、Tutorial Dでは {all from s} と書く。Erqでは {s.*} と書く。

(s join sp){all from s}
s natural join sp {s.*} distinct

拡張

Tutorial Dではこう書く。@は属性を表す。

s:{status2:=add(10,@status)}

Erqでは射影と区別せず、同じブレース記法を使えばよい。

s{s.*, status2: status + 10}

ユニオン

Tutorial Dではunion演算子を使う。

s union s

Erqでは ;SQLのunion all相当になる。

s; s

distinctを最後につけると、unionになる。

s; s distinct

s minus s

Erqには現状SQLのexcept相当の構文が存在しないが、データにnullが入っていなければ not in を使って対処できる。

s[{`s#`, sname, status, city} not in s];;

セミジョイン

Tutorial D の s semijoin sp(s join sp){all from s} と同じ。

s semijoin sp

Erqにセミジョインの記法はないが、ブラケット記法(where句)とin演算子セミジョインを表現できる。

s[{`s#`} in sp{`s#`}]

ただし、多重集合を許すErq/SQLのセマンティクス上では、x natural join y {x.*} distinctx[{c} in y{c}] は同じ結果になるとは限らない。

アンチジョイン

s のタプルのうち、セミジョイン s semijoin sp に含まれないものからなる関係が s antijoin sp

s antijoin sp

すなわち

s minus (s semijoin sp)

と同じ。

Erqでは

s[{`s#`} not in sp{`s#`}]

と書けばよい。

制限

Tutorial D

s where lt(@status, 30)

Erq

s[status < 30]

グループ・アングループ

Tutorial Dでは、グループ化するとサブリレーションを値に持った属性が作られる。Aggregate Queries

s group ({s#,sname,status} as subrel)

サブリレーションに対してアングループを行うと元に戻る。

s group ({s#,sname,status} as subrel) ungroup subrel

(個人的には、グループ化の基準になるcityが陽に指定せず、都市以外の属性を列挙することになるのが気になる。{all but city}と書けるから別にいいのだろうか。)

Erq/SQLiteではサブリレーションは存在しない。代わりにjson_group_arrayを使ったグループ化が行える。

s{city => subrel: json_group_array(json_array(`s#`, sname, status))}

JSONからのアングループも、長くなるが、一応可能となっている。

s{city => subrel: json_group_array(json_array(`s#`, sname, status))} join j: json_each(subrel) {`s#`: j.value->>0, sname: j.value->>1, status: j.value ->> 2, city}

集約

Tutorial Dではリレーション関数が用意されているので、グループ化した後に、リレーション関数を適用した属性を追加すればよい。

s group ({s#,sname,status} as subrel):{citycount:=count(@subrel)}

Erqでは、集約関数を使う。

s{city => citycount: count(*), subrel: json_group_array(json_array(`s#`, sname, status))}