🛢️

C#でSQL文を再構築(解析・加工)するライブラリを公開しました

2022/11/13に公開

静的なSQLを解析して加工する再構築(解析・加工)ライブラリを開発し、OSSで公開しましたのでご紹介します。

開発言語はC#(.NET6)です。
https://github.com/mk3008/SqModel

対象読者

  • SQLを動的に加工したいバックエンドプログラマ
  • C#、.NETプログラマ

デモ

既存のSQLをベースに、Select句とFrom句を改変。Where条件の追加を行うデモ。

var sq = SqlParser.Parse("select a.id, a.val from table_a as a");

//From句を取得します
var a = sq.FromClause;
//From句にテーブル結合を追加します
var b = a.InnerJoin("table_b").As("b").On("id");
//結合したテーブルの列をSelect句に追加します
sq.Select.Add().Column(b, "val").As("b_val");

var sql = sq.ToQuery().CommandText;

実行結果

select
    a.id
    , a.val
    , b.val as b_val
from table_a as a
inner join table_b as b on a.id = b.id

実行環境

.NET6が必要です。VisualStudio2022などでご利用ください。
パッケージのインストールにはNuGetが利用できます。

https://www.nuget.org/packages/SqModel

あくまでSQL(文字列)の解析と加工だけを行っているので、DBMS環境は不要です。

開発理由

意外なことにSQL文を再構築するライブラリは「ほとんどない」です。
私が調べた限りでは存在すら確認できていないです。

SQL Builderの現状

一般的にSQLBuilderというものは、「非SQL言語からSQL言語」への変換を指します。

「querybuilder」というライブラリを例にすると、以下のような記述にて「SQLの生成」を行ってくれます。

var books = db.Query("Books")
    .Join("Authors", "Authors.Id", "Books.AuthorId")
    .Select("Books.*", "Authors.Name as AuthorName")
    .Get();

https://github.com/sqlkata/querybuilder

上記を見ていただくと「ゼロからSQL生成している」ことがわかります。他のライブラリも細かい差異はあるにせよ同様です。

「既存のSQL文を加工する」ライブラリは寡聞にして知りません。

SQL Parserの現状

「既存のSQL文を加工する」には解析が必要です。解析は「SQLParser」と呼ばれます。
Googleにて「C# SQL Parser」などと検索していただくとわかりますが、ほとんど情報がありません。

また、「SQLParser」は「テーブル名は何か?」、「列数、列名は何か?」のように解析結果を取得するところがゴールのように見えます。

「解析結果を加工できる」ライブラリは寡聞にして知りません。

SQL Builder + SQL Parser = SQL文の再構築

「SQLBuilder」 と「 SQLParaser」は表裏の関係にあたり、親和性がものすごく高いのに両方の機能を持つライブラリは見当たりません。

同機能があれば、より自然に、より高度なSQLの加工ができるのは明白です。私はソレが欲しかったので、作りました。

何ができるのか?

解析と構築を同じクラスで行っているため、自由度の高い加工が行なえます。
以下に例を挙げます。

SQLを組み立てる(SQL Builder)

var sq = new SelectQuery();
var a = sq.From("table_a").As("a");
var b = a.LeftJoin("table_b").As("b").On("id", "table_a_id");

sq.Select.Add().Column(a, "id").As("a_id");
sq.Select.Add().Column(b, "table_a_id").As("b_id");

sq.Where.Add().Column(a, "id").Equal(":id").Parameter(":id", 1);
sq.Where.Add().Column(b, "table_a_id").IsNull();
sq.Where.Add().Column(b, "is_visible").True();

var sql = sq.ToQuery().CommandText;

実行結果

select 
    a.id as a_id
    , b.table_a_id as b_id
from table_a as a
left join table_b as b on a.id = b.table_a_id
where
    a.id = :id
    and b.table_a_id is null
    and b.is_visible = true

SQLを解析する(SQL Parser)

SelectQuery sq = SqlParser.Parse(@"select a.column_1 as col1, b.column_2 as col2 from table_a as a inner join table_b as b on a.id = b.id");

var sql = sq.ToQuery().CommandText;

実行結果

select
	a.column_1 as col1
	, b.column_2 as col2
from table_a as a
inner join table_b as b on a.id = b.id

Parseメソッドの戻り値が「SelectQuery」クラスであることに注目ください。
「SelectQuery」クラスはビルド時に使用したクラスそのものです。

SQLを再構築する(SQL Builder + SQL Parser)

デモに記述したコードを再掲します。

var sq = SqlParser.Parse("select a.id, a.val from table_a as a");

//From句を取得します
var a = sq.FromClause;
//From句にテーブル結合を追加します
var b = a.InnerJoin("table_b").As("b").On("id");
//結合したテーブルの列をSelect句に追加します
sq.Select.Add().Column(b, "val").As("b_val");

var sql = sq.ToQuery().CommandText;

実行結果

select
    a.id
    , a.val
    , b.val as b_val
from table_a as a
inner join table_b as b on a.id = b.id

結合式の差し込み、列の差し込みが確認できます。

まとめ

今回は結合式と列の差し込みを加工サンプルとして挙げましたが、同ライブラリを使うことで

  • From句のテーブル名を書き換える
  • 現在のクエリをWith句に変換する

といった、従来とは別次元の加工(文字列操作)が行なえます。

ご興味がある方はお試しください。

Discussion