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.*} distinct
と x[{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))}