日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

Clojure の SQL DSL の一つ sqlingvo なかなかイイですよ

[2014-07-22 追記] table 別名がデフォルトで AS なしに修正された事を示すリンクを追記

Clojure 界隈では、SQL DSL の選択肢が結構前からいくつか存在する。初期からある KormaHoneySQL そして今は ClojureJDBC ラッパーのデファクトスタンダートとなってる clojure.java.jdbc でもごく簡易的な DSL を備えている

今回はその中でも sqlingvo を使ってみたのでその感想を書いてみる。

今回はあくまで感想のみ。他ライブラリとの比較は使ってないのでできないし、 インストールやごく基本的な使い方に関しても公式を見れば大体分かると思うので書かない。

良いと思った所

WITH 句、CASE 式対応

個人的に決め手になったのはこれ。特に WITH 句はこいつが対応してるとアプリ側でなんちゃって View のようなものを共通で持つ事が簡単になる*1

他にも DELETE, INSERT, UPDATE はじめ、実用するに必要なもの一通りが揃っている感じ。

CASE 式もクエリで取る段階で判定して処理しとけるならしときたいよね、と言う場面はちょいちょ いあるのでこれは嬉しい。勿論 CASE 無しで取っておいてアプリ側でループで条件判断しながらぶん 回せば同じ事はできるけど。けどなあ。

素の SQL にかなり近い

例えばこういうの。

(select [1 2 3] (from :dual))

SQL 嫌いじゃないんでそんな Clojure っぽくマップデータ化とかする必要ないと思うんです。

役割としては sqlingvo の SQL DSLclojure.java.jdbc で扱い易い形に変換するだけ

clojure.java.jdbc で扱い易い形と言うのはこういうの↓

(sql (select [1] (from :dual)))
;=> ["SELECT 1 FROM \"dual\""]

ベクタで先頭要素がパラメータ付き SQL 文字列、残り要素がパラメータに渡す値と言う形のやつ。

つまり実際に DB からデータを取ってくるには clojure.java.jdbc も一緒に使う必要がある。 だけどこれまでも clojure.java.jdbc 使って生 SQL 投げていた自分にとっては殆ど問題にならない。

SQLClojure 上で扱いやすく書ける機能のみの提供と言うのかな。

これは言い替えると実際のスキーマに存在しているかどうかは sqlingvo では全くチェックしないの で注意するとしたらそれくらいか。

微妙だと思った所

Oracle との親和性が若干悪い

どっちかっつーとこの 2 つは Oracle 対応しろよと言う感じなのかな。標準仕様どこで読めるか分 からんから何とも言えんけど。10 以降だと対応してるのかも?

SQL 文字列展開時に DB オブジェクト名がダブルクォーテーションで括られるのに Oracle9i が対応してない

最初の変換例にあるような形そのままのは Oracle(9i) では許しちゃくれません。 なので Oracle の時はダブルクォーテーションを外すヘルパー関数をかましてやる必要がある。

FROM 句でのテーブル別名で AS を入れるが Oracle9i が(ry

昔ながらの (+) で表結合するスタイルの人はこれでもう選択肢から外れるのかな。 ただこれも JOIN で行う結合においてもサブクエリが使えないという制限がかかってしまうことになる。

[2014-07-22 追記] 0.5.17 以降は table 別名はデフォルトで AS 無しに修正されたようです

ちょっとマクロシンタックスが多くない?

結構量がある テストコード は sqlingvo の場合書き方のサンプルにもなってて、これ眺めてると分かるが、それなりに複雑なク エリになるとクォート・アンクォート・バッククォートも併用しないとちゃんと変換してくれない。

何かマクロを書いているような気分になってきてなんだかなあ、と言うのはちょっとあるw

HAVING 句のサポート

できれば欲しい。使わない書き方にするのも割と簡単だけども。

最後に

微妙と思った点などで挙げたように一部制限やクセはあるものの、クエリの一部を取り出して使い回 したいと思っていた人には結構オススメ。SQL の弱点と個人的には思っている分割粒度がどうしてもデ カくなるのを上手く補っているライブラリじゃないかなと。

*1:共通で持てる SQL 文字列を分割してるようなもの なので実際データ取る時は毎度リクエストを投げるが