SQLを書いていていろんな場面で「こんな文法であってくれたら!」と歯がゆい思いをすることは多々あります。
これまでに、こう拡張されてくれたらと思った内容をまとめてみました。
なお、この拡張は各社DBの“方言”とはまた別のものです。各社方言とは直交した拡張です。
また、純粋なテキスト変換処理で現行のSQLに変換できるようにというのも意図しています。
dangling comma, その他,
カンマ区切りでカラムを列挙するときに、リストの最初と最後に無意味なカンマを許します。
SELECT col1, col2, -- ← これがdangling comma FROM some_table
こういう書き方が許されるというもの。カラム構成を編集するときにコピペ操作がしやすくなります。また、gitの変更履歴が見やすくなります。
SELECT
, ORDER BY
, GROUP BY
などのカラム定義リストのほか、IN
句の値リスト、関数呼び出しの引数リスト、WITH
句のCTEリストも対象です。
カンマのほか、AND
, OR
, UNION [ALL]
, INTERSECT [ALL]
についても同様の扱いをします。
空のWHERE, その他
WHERE
, HAVING
, ON
句に何も条件式を書かないことを許します。
これは、句ごと省略されたものと解釈されます。
字句解析がちょっと面倒なことになるので、()
は書いてもらうことにしましょう。
すると、
SELECT * FROM some_table WHERE ( ${conditions} )
のように動的に絞込条件を組み上げる際に、conditions
が空であれば
SELECT * FROM some_table
だと解釈してもらえるということです。
ここでいう動的な組み上げというのは必ずしもSQLインジェクション脆弱性を生む組み上げ操作というわけではありません。
プリペアドステートメントを組み上げるような場合とお考えいただければ。
空のIN句
col1 IN ()
という書き方を許し、この式は常にFALSEとなるというもの。左辺がNULL
であってもFALSE。
もちろん目的は動的なIN
句の生成に空リストを渡せるように。
IN, ANY, SOME, ALL(テーブル)
ある値が指定テーブル内に見つかるかを調べるとき
col1 IN (SELECT col2 FROM other_table)
と書くわけですが、other_table
に1カラムしかないことがわかっているのなら
col1 IN (SELECT * from other_table)
でもOKです。 これを
col1 IN other_table
と書いても良いという拡張。特にother_table
がCTEである場合に使いやすいです。1カラムしかないことはこのSQL内で保証できますからね。
EXISTS(テーブル)
上記とほぼ同様。
EXISTS(SELECT 1 FROM some_table)
を
EXISTS some_table
と書かせてくれよという拡張。
クロスIN
それマルチカラムアトリビュートやってるのを解消すべきじゃないのと怒られることが多そうではありそうですが
(col1 IN (v1, v2, v3) OR col2 IN (v1, v2, v3))
みたいな式を
SOME col1, col2 IN (v1, v2, v3) -- または ANY col1, col2 IN (v1, v2, v3)
と書けるようにという拡張。 同様に、
ALL col1, col2 IN (v1, v2, v3)
は
(col1 IN (v1, v2, v3) AND col2 IN (v1, v2, v3))
と解釈されます。
JOIN USING, JOIN ON
結合条件はSQLだと
table1 JOIN table2 ON 結合条件 -- または table1 JOIN table2 USING (結合カラム)
と指定するものですが、次のように書けた方が編集しやすいです。
table1 JOIN ON (結合条件) table2 -- または table1 JOIN USING (結合カラム) table2
だって、結合段数が多いとき、
FROM users u JOIN USING(user_id) blog b JOIN USING(blog_id) articles a JOIN ON(a.article_id = c.article_id) comments c
となりますから、行単位で入れ替えをしても式を壊してしまいにくくなりますし見通しも良くなります。
ON
句の末尾の判定が難しくなるので、ON
句の条件部分を括弧でくくるのは必須になりますね。
(現行のSQLでもON句の条件部分を括弧でくくるのは良い書き方だと思います)
FROM 〜 TO 〜
BETWEEN 〜 AND 〜
演算子の使い勝手、悪いと思いません?
「A以上B以下」なので時刻指定の問い合わせでよく使う「A以上B未満」に使えないのです。
そこでFROM 〜 TO 〜
演算子。条件式を書くコンテキストに限っては
x FROM a TO b
が
(a <= x AND x < b)
と解釈されます。
「条件式を書くコンテキスト」ってのが微妙なんですけどね。 PostgreSQLみたいにbool型があって条件式とbool型の計算式の区別がないDBにとっては、WHERE句内とかCASE WHEN句内ではこのFROM-TOが書けるのにSELECT句内でbool型の計算式を書いているときにはFROM-TOが使えませんってなって統一性を欠くことに。
SELECT句を最後に書ける
SELECT
がクエリの最初に来るの、ほんとおかしくありません?
だって、クエリを頭から読んでいってまずカラム名のリストを読まされるんだけど何のテーブルだか分からないからそれがカラム名であるかさえよくわからないし型も当然わからない。
先を読まないと意味が確定しない記述なんてプログラミング言語として最低です。
そのくせORDER BY
句とかGROUP BY
句では、SELECT
句で定義して命名したカラムが使えないんですよ。すでに定義が明かなキーワードが逆に使ってもらえないという本末転倒ぶり。
SELECT
句が最後に来ればすべてはすっきりするのです。(CTEのことはおいておいて)FROM
から書き始め、最後の最後、GROUP BY
よりもORDER BY
よりもHAVING
よりもLIMIT
よりも後にSELECT
が来る。
そう書かせてくださいよと。おっとそれどこのLINQ。