ここまでのあらすじ
ScriptDom っていう、SQLを編集するためのライブラリがあるらしい。
これを使って、複数のクエリを集合演算子でUNIONしたりできる部品を作ってみたよ。
前回の記事はこちら↓
https://qiita.com/K-Z/items/a0a85a8498f0baaa94be
ScriptDom
https://www.nuget.org/packages/Microsoft.SqlServer.TransactSql.ScriptDom/15.0.4200.1
実際の開発で動的にSQLを編集したいシーンってどんなとき?
一番多いのは画面入力などの外部入力に応じてWHERE句に条件を追加したりするときだと思います。
ついで、ソート順をいじくりたいときかな……。
そういう訳で、今回はWHERE句の編集ができるように部品に機能追加をしていきます。
前回同様、コードの解説は少なめです。
ご承知おきください。
さて、イメージとしては、こんな感じのコードでWHERE句に条件式を追加出来るようにしたいのだが……。
usingKzLib.SqlServer.TransactSql.ScriptDom;////////////// (中略) ////////////////stringbaseSql=@"SELECT
Foo1.Bar1
FROM Foo
WHERE
Foo1.Bar1 = 1";TransactSQLtSQL=TransactSQL.Parse(baseSql);tSQL.Items[0].WhereClause.Items.Add(newWhereItem(@"Foo1.Bar2 = '2'",BooleanBinaryExpressionType.And));System.Diagnostics.Debug.Write(tSQL.ToString());
SQLの一部分をパースする
さて、この要件を満たすには、後から追加する条件式の文字列をパース出来る必要があります。
このようなSQLスクリプトの一部分をパースするためのAPIもScriptDomにはいくつか存在します。
今回使うのは、
TSql150Parser.ParseBooleanExpression
メソッドです。
これが、条件式をパースするためのメソッドになります。
もうひとつ、左右の値を比較するような条件式を書くときに、定数値やら、Hoge.Huga のようなテーブルの列の値を記述したりします。
これは、
TSql150Parser.ParseExpression
メソッドを使うとパースできます。
条件式の構造
さて、条件式はパースした結果、どのように格納されているのでしょう。
前回、クエリを UNION で結合する部品を作ったときには、クエリがツリー上に格納されていました。
やはり、WHERE句の条件式もこれと同様にツリー上に格納されています。
条件式を表すクラスの親玉として、
BooleanExpression
というクラスがあり、条件式の種類に応じてそのサブクラスがいくつかあります。
BooleanExpression
https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.sqlserver.transactsql.scriptdom.booleanexpression?view=sql-dacfx-140.3881.1
これらのサブクラスのうち今回の目的で特に重要なのは、BooleanBinaryExpression と、BooleanParenthesisExpression です。
前者は2つの条件式を And や Or で繋ぐ論理演算を表します。
後者は条件式中の1部を優先的に評価すること(つまり、括弧の役割)を表します。
例えば次のようなSQLのWhere句
WHEREHoge1.Huga1=1--条件式(1)AND(Hoge1.Huga1=2--条件式(2)ORHoge1.Huga1=3--条件式(3)ORHoge1.Huga1=4--条件式(4))
これをパースすると、WHERE句は以下のようなイメージの式ツリーになります。
- BooleanBinaryExpression(And)
- 条件式(1)
- BooleanParenthesisExpression
- BooleanBinaryExpression(Or)
- 条件式(2)
- BooleanBinaryExpression(Or)
- 条件式(3)
- 条件式(4)
- BooleanBinaryExpression(Or)
例によって無駄に(?)階層が深くなって人間には分かりにくく、編集しづらそうです。
人間に分かりやすくするなら、こんな感じでしょうか。
- 条件式(1)
- (AND)
- 条件式(2)
- (OR)条件式(3)
- (OR)条件式(4)
ということで……。
おわりに
そんなこんなで、必要なAPIや目標とする部品のイメージは判明したので、それを作ったものがこちらになります(3分クッキング)。
https://github.com/8810hayate/TransactSQL
ところで、BooleanExpression のサブクラスを眺めていると、役割が名前からすぐに分かるものと分からない物がありました。
調べてみると、意外と普段何気なく使っているSQLにも知らない構文があったりしそうだなぁと感じました。