空いていたので穴埋め。
はじめに
データベースを扱うプロジェクトでは、オンコードで SQL を書く事が割と多いのですが、そういったソースコードに埋め込まれた SQL はプレースホルダを使って値を取るので直接実行する事は出来ません。
select * from foo where id = :id and bar = :bar
RDBMS によっては :変数名
ではなく ?
や $1
といった表記をする物もあります。こういった SQL は直接実行できない為、どうしても結果がイメージし辛くなるのですが、2 Way SQL という方法を使う事で解決できる事があります。
2 Way SQL とは
2 Way SQL は SQL のコメントに IF や ELSE、END といった制御構文を埋め込む事で、直接実行する事も出来るし、プレースホルダを使ったオンコード用の SQL としても使う事ができるといった物です。筆者が知る限り明確な仕様は見当たらないのですが、昔は Doma/S2Dao といったデータベースの抽象化レイヤと合わせて利用されて来ました。
select * from foo where id = /*id*/5 /* IF enabled */and bar = /*bar*/'foo' /*END*/
この SQL を実行するとコメントは無視される為、実際は以下が実行されます。
select * from foo where id = 5 and bar = 'foo'
このコメントに記述された IF/ELSE/END と、値の直前に書かれたコメントが 2 Way SQL です。
2 Way SQL のルール
select * from foo where id = /*id*/5 /* IF enabled */and bar = /*bar*/'foo' /*END*/
この SQL に制御パラメータ enabled
を true 渡すと IF 文が有効になるため、id と bar による抽出が有効になります。また false で渡すと IF 文が無効となるため id のみの抽出となります。
select * from foo where id = :id
この様に /*bar*/'foo'
はプレースホルダ bar
に置き換えられます。
2 Way SQL の実装
随分前ですが golang で実装しました。
以下の様に使います。
env := make(map[string]interface{})
env["enabled"] = true
sql2way := `
select
*
from
foo
where
id = /*id*/5
/* IF enabled */
and bar = /*bar*/3
/*END*/
`
sql, err := Eval(sql2way, env, ways2go.Question)
正常に評価されると以下の SQL を得る事が出来ます。
select
*
from
foo
where
id = ?
and bar = ?
データベースによってはプレースホルダの形式が異なります。ways2go では以下の形式をサポートしています。
識別 | プレースホルダ |
---|---|
Question | ? |
Dollar | $1 |
Colon | :foo |
これを使えばサンプルとして実行する事もでき、プログラムから実行する事もできる SQL を同じファイルで管理する事が出来ます。
まとめ
大規模なプロジェクトで SQL が散乱する様なコードベースでは SQL を外部ファイルに切り出すことでソースコードがスッキリするかもしれません。ただし実は筆者も昔に作ったまま時間が経っており、テストは書いてあるのですが実務で使った事がありません。皆さんからの PR や機能追加をお待ちしております。