ささいなことですが。

Windowsアプリテスト自動化ライブラリFriendly開発者の日記です。

SQLWorld★大阪#38 に参加してきました。

SQL力を高めるべく、SqlWorld :: SQLWorld★大阪#38に参加してきました!
簡単なものから高度すぎるものまで、レンジの広い問題が出題されました。
で、その中でこんな問題がありました。
友達の人数を抽出するというものです。
f:id:ishikawa-tatsuya:20160825000048p:plain
で、私はこんなSQLを書きました。

SELECT 人.名前,
       isnull(友達人数.人数, 0) AS 人数
FROM   人
       LEFT OUTER JOIN
       (SELECT   友達一次元.ID AS ID,
                 sum(人数) AS 人数
        FROM     (SELECT   人ID1 AS ID,
                           count(*) AS 人数
                  FROM     友達
                  GROUP BY 人ID1
                  UNION
                  SELECT   人ID2 AS ID,
                           count(*) AS 人数
                  FROM     友達
                  GROUP BY 人ID2) AS 友達一次元
        GROUP BY 友達一次元.ID) AS 友達人数
       ON 人.ID = 友達人数.ID;

LambdicSqlで書いてみます

まあ、サブクエリの入れ子とか複雑ですよね。これをLambdicSqlで書いてみようと思います。(実はもっと良い書き方を主催のおださんから教えていただいたのですが、LambdicSqlの題材としてはこっちの方がよかったので)
LambdicSqlは順に組み立てられるのでこんなとき便利です。
最後のToSqlInfoでがSQL文字列が作成されます。

//テーブルの定義
class 人
{
    public int ID { get; set; }
    public string 名前 { get; set; }
}
class 友達
{
    public int 人ID1 { get; set; }
    public int 人ID2 { get; set; }
}
class DB
{
    public 人 人 { get; set; }
    public 友達 友達 { get; set; }
}

[TestMethod]
public void TestSqlWorld()
{
    //①友達テーブルの人ID1の列で友達の数を数えるクエリ
    var 人数1 = Sql<DB>.Create(db =>
        Select(new
        {
            ID = db.友達.人ID1,
            人数 = Count<int>(new Asterisk())
        }).
        From(db.友達).
        GroupBy(db.友達.人ID1));

    //②友達テーブルの人ID2の列で友達の数を数えるクエリ
    var 人数2 = Sql<DB>.Create(db =>
        Select(new
        {
            ID = db.友達.人ID2,
            人数 = Count<int>(new Asterisk())
        }).
        From(db.友達).
        GroupBy(db.友達.人ID2));

    //③合算して一列のテーブルにするクエリ
    var 友達一次元 = 人数1.Union(人数2);

    //④IDでグルーピングして人数を数える
    var 友達人数 = Sql<DB>.Create(db =>
    Select(new
    {
        ID = 友達一次元.Body.ID,
        人数 = Sum(友達一次元.Body.人数)
    }).
    From(友達一次元).
    GroupBy(友達一次元.Body.ID));

    //⑤クエリ完成
    //名前を付けるクエリ
    var 友達人数_名前付き = Sql<DB>.Create(db =>
        Select(new
        {
            名前 = db.人.名前,
            人数 = IsNull(友達人数.Body.人数, 0)
        }).
        From(db.人).
        LeftJoin(友達人数, db.人.ID == 友達人数.Body.ID)
    );

    //文字列とパラメータの取得
    //後はDapperへ
    var info = 友達人数_名前付き.ToSqlInfo(typeof(SqlConnection));
    Debug.Print(info.SqlText);
}

こんなSQLになります。

SELECT
	人.名前 AS 名前,
	ISNULL(友達人数.人数, @p_1) AS 人数
FROM 人
	LEFT JOIN 
	(SELECT
		友達一次元.ID AS ID,
		SUM(友達一次元.人数) AS 人数
	FROM 
		(SELECT
			友達.人ID1 AS ID,
			COUNT(*) AS 人数
		FROM 友達
		GROUP BY 友達.人ID1
		UNION
		SELECT
			友達.人ID2 AS ID,
			COUNT(*) AS 人数
		FROM 友達
		GROUP BY 友達.人ID2) 友達一次元
	GROUP BY 友達一次元.ID) 友達人数 ON (人.ID) = (友達人数.ID)

履歴

2016/09/02 β版対応

LambdicSql -主要DBゆるふわ対応-

LambdicSql_α0.0.63をリリースしました。β版間近です!
www.nuget.org

マルチDB対応

なんと主要6DBで動作確認しています。Surfaceに全部インストールしましたが、意外と入るものですね。(SQLiteはインストール不要)

DataBase type 動作確認
SQL Server
SQLite
PostgreSQL
Oracle
MySQL
DB2

とは言え、同じ書き方でOKなわけではないです。

この辺がゆるふわ。LambdicSqlは基本はそのままSQLのテキストになります。つまり、普通にSQL書く時と同じように使える句や関数だけ使うという方針です。

public void TestWindow()
{
    //SQLiteとMySqlはWindow関数使えないよ。
    if (_connection.GetType().FullName == "System.Data.SQLite.SQLiteConnection") return;
    if (_connection.GetType().FullName == "MySql.Data.MySqlClient.MySqlConnection") return;

    //make sql.
    var query = Db<DB>.Sql(db =>
        Select(new SelectData()
        {
            Avg = Window.Avg(db.tbl_remuneration.money).
                    Over<decimal>(new PartitionBy(db.tbl_staff.name, db.tbl_remuneration.payment_date),
                        new OrderBy(new Asc(db.tbl_remuneration.money), new Desc(db.tbl_remuneration.payment_date)),
                        new Rows(1, 5)),
            PaymentDate = db.tbl_remuneration.payment_date,
            Money = db.tbl_remuneration.money,
        }).
        From(db.tbl_remuneration).
        Join(db.tbl_staff, db.tbl_remuneration.staff_id == db.tbl_staff.id));

    //to string and params.
    var info = query.Build(_connection.GetType());
    Debug.Print(info.SqlText);

    //dapper
    var datas = _connection.Query<SelectData>(info.SqlText, info.Params).ToList();
}

オプションで、以下を切り替えれるようにしました。

じゃあ、何がマルチDB対応なんだよってことなのですが、以下の二つだけオプションで切り替えれるようにしています。こればっかりは書き分けられないので・・・。

  • 文字列の+演算
  • パラメータのプレフィックス
[TestMethod]
public void Test()
{
    var query = Db<DB>.Sql(db =>
        Select(new
        {
            Name = db.tbl_staff.name + "★",
            Money = db.tbl_remuneration.money,
        }).
        From(db.tbl_remuneration).
            Join(db.tbl_staff, db.tbl_staff.id == db.tbl_remuneration.staff_id).
        Where(1000000 < db.tbl_remuneration.money));

    //文字列化するときにオプションを指定する
    var info = query.Build(new SqlConvertOption() { ParameterPrefix = ":", StringAddOperator = "||" });
    Debug.Print(info.SqlText);
}
SELECT
	/*文字列結合の演算子に||を指定*/
	(tbl_staff.name) || (:p_0) AS Name,
	tbl_remuneration.money AS Money
FROM tbl_remuneration
	JOIN tbl_staff ON (tbl_staff.id) = (tbl_remuneration.staff_id)
/*オラクルは@使えない*/
WHERE (:p_1) < (tbl_remuneration.money)

なんで、こんなとこに差だすかなー。オラクルで@使ったらダメとか、わからなくて結構悩みました。

DataBase type 文字列結合 パラメータプレフィックス
SQL Server + @
SQLite || @
PostgreSQL || @
Oracle || :
MySQL + @
DB2 || @

これを毎回指定するのは面倒なんで、コネクション(SqlConnectionとか)のタイプを渡すと最適なオプションを選択するようにしています。

//コネクションタイプを渡すと、最適なオプションを選択します。
var info = query.ToSqlInfo(connection.GetType());

同一の記法でSQLテキストをカスタマイズする方法も提供しています。

長くなるので端折りますが、ToSqlInfoの第三引数にテキスト出力をカスタマイズするインターフェイスを渡せるようにしています。どうしてもやりたい人はこちらでできるように。また句や関数を自分で増やせる手段も提供しています。(拡張オレオレ句も作れる)

//第三引数でカスタマイズインターフェイスを渡せる仕様にしてます。
public static SqlInfo ToSqlInfo(this ISqlExpression exp, SqlConvertOption option, ISqlSyntaxCustomizer customizer)

LambdicSql - EntityFrameworkと仲良くなりました。

EntityFrameworkを使っているときでも、たまにSQLを直に書きたくなる場合がありますよね。そんな時はLambdicSqlを使うと便利です。そして、EntityFrameworkを使っている環境からLambdicSqlを使いやすい工夫を入れました。しかもそれでいてLambdicSql自体はDLL的にEntityFrameworkに依存したりはしていません。

DataContextクラスを指定できるようにしました。

EntityFrameworkでDBからモデルクラスを生成すると、こんな感じのものが生成されます。(若干省略してます)

public partial class tbl_remuneration
{
    public int? staff_id { get; set; }
    public string payment_date { get; set; }
    public int id { get; set; }
    public decimal? money { get; set; }
}

public partial class tbl_staff
{
    public int id { get; set; }
    public string name { get; set; }
}

public partial class ModelLambdicSqlTestDB : DbContext
{
    public virtual DbSet<tbl_remuneration> tbl_remuneration { get; set; }
    public virtual DbSet<tbl_staff> tbl_staff { get; set; }
}

これはLambdicSqlの定義に似ています。これをそのまま使えるようにしました。

public void TestEFAndLambdic()
{
    var query = Db<DB>.Sql(db =>
        Select(new
        {
            name = db.tbl_staff.T().name,
            payment_date = db.tbl_remuneration.T().payment_date,
            money = db.tbl_remuneration.T().money,
        }).
        From(db.tbl_remuneration.T()).
            Join(db.tbl_staff, db.tbl_remuneration.T().staff_id == db.tbl_staff.T().id);

    var datas = _connection.Query<SelectData1>(query).ToList();
}

T()がポイントですね。DbSet<>をスルーするものです。次のようなSQLになります。

SELECT
	tbl_staff.name AS "name",
	tbl_remuneration.payment_date AS "payment_date",
	tbl_remuneration.money AS "money"
FROM  tbl_remuneration
	JOIN tbl_staff ON (tbl_remuneration.staff_id) = (tbl_staff.id)

上記の例では素直にEFで書けばよいのですが、例えばOR結合を条件によって組み立てるときとか、Window関数を使うときとか、EFでは書きづらいもので利用してもらえばよいと思います。

//ORを任意の条件で組み立てるときとか
var exp = Sql<DB>.Create(db =>
    Condition(minCondition, 3000 < db.tbl_remuneration.T().money) ||
    Condition(maxCondition, db.tbl_remuneration.T().money < 4000));
var query1 = Sql<DB>.Create(db => SelectFrom(db.tbl_remuneration.T()).Where(exp));

//Window関数を使いたいときとか
var query2 = Db<DB>.Sql(db =>
    Select(new SelectData()
    {
        Average = Window.Avg(db.tbl_remuneration.T().money).
                Over<decimal>(null,
                    new OrderBy(new Asc(db.tbl_remuneration.T().payment_date)),
                    null)
    }).
    From(db.tbl_remuneration.T()));

名前解決のルールは若干違うので気を付けてください。

LambdicSqlのルールは、こちらです。EntityFrameworkとはテーブル名のルールが違っていますね。EFの場合はクラス名とテーブル名を対応させるルールで、しかも複数形と単数形の解決とか面倒なことをやってますね。とは言え、普通に生成するとEFも変数名とテーブル名は一致します。TableAttributeで指定している場合はLambdicSqlも同じルールで解決するので問題ないですね。問題ある命名している場合は、すみませんがテーブル定義を並べるクラスだけ別途作成お願いします。

是非EntityFrameworkユーザーの方もご利用お願いします!

LambdicSqlはメインでも使えて、脇役に回ってもいい仕事をするライブラリを目指しております。

LambdicSql - TableAttributeとColumnAttributeに対応しました。

LambdicSql_α0.0.53をリリースしました。
www.nuget.org

テーブル名とカラム名のルール

LambdicSqlのテーブル名とカラム名は、通常は変数名で表します。

public class Staff
{
    //変数名がカラム名になる
    public int id { get; set; }
    public string name { get; set; }
}
public class Remuneration
{
    public int id { get; set; }
    public int staff_id { get; set; }
    public DateTime payment_date { get; set; }
    public decimal money { get; set; }
}
public class DB
{
    //テーブル名も変数名で表す
    public Staff tbl_staff { get; set; }
    public Remuneration tbl_remuneration { get; set; }
}

スキーマを使う場合も変数名で表します。(もちろんスキーマを書く必要がない場合はこれは省略できます)

public class DBO
{
    public Staff tbl_staff { get; set; }
    public Remuneration tbl_remuneration { get; set; }
}
public class DB_EX
{
    //スキーマも変数名で表す
    public DBO dbo { get; set;}
}
//スキーマまで書きたい場合
var query = Sql<DB_EX>.Create(db =>
    Select(new
    {
        Name = db.dbo.tbl_staff.name,
        PaymentDate = db.dbo.tbl_remuneration.payment_date,
        Money = db.dbo.tbl_remuneration.money,
    }).
    From(db.dbo.tbl_remuneration).
        Join(db.dbo.tbl_staff, db.dbo.tbl_staff.id == db.dbo.tbl_remuneration.staff_id));

var info = query.ToSqlInfo(_connection.GetType());
Debug.Print(info.SqlText);
SELECT
	dbo.tbl_staff.name AS "Name",
	dbo.tbl_remuneration.payment_date AS "PaymentDate",
	dbo.tbl_remuneration.money AS "Money"
FROM dbo.tbl_remuneration
	JOIN dbo.tbl_staff ON (dbo.tbl_staff.id) = (dbo.tbl_remuneration.staff_id)

TableAttributeとColumnAttributeに対応しました。

それに加えてTableAttribute、ColumnAttributeでも名前を指定できるようにしました。
TableAttribute、ColumnAttributeは System.ComponentModel.DataAnnotations.dll に定義されている標準のものです。
テーブル名とか変数名をラムダ中で短く書きたい場合や、スキーマを一段クラスで表現するのが面倒な場合、それからテーブルやカラムの名前がC#の変数名で使えないものだったりする場合に利用できると思います。

//テーブル名をクラス定義時に指定できる
[Table("tbl_staff")]
public class StaffX
{
    //属性の方が優先される
    [Column("id")]
    public int idx { get; set; }
    [Column("name")]
    public string namex { get; set; }
}
public class DB
{
    //StaffXは属性でテーブル名を持っているのでそちらが優先される
    public StaffX xxx { get; set; }
    public Remuneration tbl_remuneration { get; set; }
}
var query = Db<DB>.Sql(db =>
    Select(new
    {
        name = db.xxx.namex,
        payment_date = db.tbl_remuneration.payment_date,
        money = db.tbl_remuneration.money,
    }).
    From(db.tbl_remuneration).
        Join(db.xxx, db.tbl_remuneration.staff_id == db.xxx.idx);

var info = query.Build(_connection.GetType());
Debug.Print(info.SqlText);
SELECT
	tbl_staff.name AS "name",
	tbl_remuneration.payment_date AS "payment_date",
	tbl_remuneration.money AS "money"
FROM tbl_remuneration
	JOIN tbl_staff ON (tbl_remuneration.staff_id) = (tbl_staff.id)

LambdicSql - α51 その他

パラメータ名称

@p_0って感じで連番にしていました。折角変数名を使っているのだから、それを変数名に使った方が良いとの意見をいただそのようにしました。

public void ParamName()
{
    var min = 3000;
    var max = 4000;

    var query = Db<DB>.Sql(db =>
        Select(new
        {
            money = db.tbl_remuneration.money,
        }).
        From(db.tbl_remuneration).
        Where(min < db.tbl_remuneration.money && db.tbl_remuneration.money < max));

    var info = query.Build(cnn.GetType());
    Debug.Print(info.SqlText);
}
SELECT
	tbl_remuneration.money AS "money"
FROM tbl_remuneration
WHERE ((@min) < (tbl_remuneration.money)) AND ((tbl_remuneration.money) < (@max))

ただ、一つの関数内で完結していれば、問題ないのですが、LambdicSqlは式を組み合わせることができるので、変数名も重複する可能性があります。
その場合は、_がついていくことになります。

public void ParamName2()
{
    var min = 3000;
    var max = 4000;

    var query = Db<DB>.Sql(db =>
        Select(new
        {
            money = MoneyExp(5, 10).Cast<decimal>(),
        }).
        From(db.tbl_remuneration).
        Where(min < db.tbl_remuneration.money && db.tbl_remuneration.money < max));

    var info = query.Build(cnn.GetType());
    Debug.Print(info.SqlText);
}
public ISqlExpression<decimal> MoneyExp(int min, int max)
    => Sql<Data>.Create(db => db.tbl_remuneration.money + min + max);
SELECT
	((tbl_remuneration.money) + (@min)) + (@max) AS "money"
FROM tbl_remuneration
WHERE ((@min_) < (tbl_remuneration.money)) AND ((tbl_remuneration.money) < (@max_))

Window関数対応

Window関数に対応しました。こんな感じで使えます。

public void TestWindow()
{
    var query = Db<DB>.Sql(db =>
        Select(new SelectData()
        {
            Avg = Window.Avg(db.tbl_remuneration.money).
                    Over<decimal>(null,
                        new OrderBy(new Asc(db.tbl_remuneration.payment_date)),
                        null),
            PaymentDate = db.tbl_remuneration.payment_date
        }).
        From(db.tbl_remuneration));

    //to string and params.
    var info = query.Build(_connection.GetType());
    Debug.Print(info.SqlText);

    //dapper
    var datas = _connection.Query<SelectData>(info.SqlText, info.Parameters).ToList();
}
SELECT
	AVG(tbl_remuneration.money)OVER(
	ORDER BY
		tbl_remuneration.payment_date ASC) AS "Avg",
	tbl_remuneration.payment_date AS "PaymentDate"
FROM tbl_remuneration

一気に書いても良いのですが、以下のように分けて書くことも可能です。

var avg = Db<DB>.Sql(db => Window.Avg(db.tbl_remuneration.money).
                Over<decimal>(null,
                    new OrderBy(new Asc(db.tbl_remuneration.payment_date)),
                    null));
//make sql.
var query = Db<DB>.Sql(db =>
    Select(new SelectData()
    {
        Avg = avg,
        PaymentDate = db.tbl_remuneration.payment_date
    }).
    From(db.tbl_remuneration));

それでWindow関数の書式なのですが、以下のようなものになっています。Window関数はいくつかは普通の集約関数と同名ですので、using staticはしない方が良いですね。

var avg = Db<DB>.Sql(db =>
        //関数
        Window.Avg(db.tbl_remuneration.money).
        //不要な部分はnullを入れる
        Over<decimal>(
            //Partitionを指定 複数指定可能
            new PartitionBy(db.tbl_staff.name, db.tbl_remuneration.payment_date),
            //OrderByを指定 複数指定可能
            new OrderBy(new Asc(db.tbl_remuneration.money), new Desc(db.tbl_remuneration.payment_date)),
            //Rowsを指定 引数は一つの場合はPRECEDINGのみの指定となる
            new Rows(1, 5)));

LambdicSql - 文字列連携、そして2WaySQL -

LambdicSqlはLambdaでSQLを表現することを目的にしています。でも、やっぱり文字列も使えた方が安心感がありますよね。てわけで文字列を埋め込める機能を追加しました。

文字列を式に変換

こんな感じで文字列を途中に埋めれます。ただ文字列を入れるだけではなく、そこにラムダで表現した情報を入れれるのです。

public void TestFormatText()
{
    var query = Db<DB>.Sql(db =>
        Select(new SelectData()
        {
            name = db.tbl_staff.name,
            payment_date = db.tbl_remuneration.payment_date,
            //好きな文字列を入れることができる
            money = Text<decimal>("{0} + {1}", db.tbl_remuneration.money, 1000),
        }).
        From(db.tbl_remuneration).
            Join(db.tbl_staff, db.tbl_remuneration.staff_id == db.tbl_staff.id).
        Where(3000 < db.tbl_remuneration.money && db.tbl_remuneration.money < 4000));

    //to string and params.
    var info = query.Build(_connection.GetType());
    Debug.Print(info.SqlText);

    //dapper
    var datas = _connection.Query<SelectData1>(query).ToList();
}
SELECT
	tbl_staff.name AS "name",
	tbl_remuneration.payment_date AS "payment_date",
	/*↓こんな感じで合成される*/
	tbl_remuneration.money + @p_0 AS "money"
FROM tbl_remuneration
	JOIN tbl_staff ON (tbl_remuneration.staff_id) = (tbl_staff.id)
WHERE ((@p_1) < (tbl_remuneration.money)) AND ((tbl_remuneration.money) < (@p_2))

2WaySQL

これは総監督から強い要望があった機能です。2WaySQLという考え方があるようです。Javaの界隈ではDomaというライブラリが有名なようです。

  1. テキストでベースのSQLを書く
  2. 動的に変えたいところにコメントで印をつける
  3. プログラムで動的に置換

ポイントは2.の時点でコメントで印をつけただけなので、そのままDBMSで実行することができるという点です。基本のSQLだけでも確認取れてた方が安心感が増しますよね。LambdicSqlではシンプルに/*no*/~/**/の間を置き換えるというルールにしました。

public void TestFormat2WaySql(bool minCondition, bool maxCondition, decimal bonus)
{
    var sql = @"
SELECT
tbl_staff.name AS name,
tbl_remuneration.payment_date AS payment_date,
tbl_remuneration.money + /*0*/1000/**/ AS money
FROM tbl_remuneration 
JOIN tbl_staff ON tbl_staff.id = tbl_remuneration.staff_id
/*1*/WHERE tbl_remuneration.money = 100/**/";

    var query = Db<DB>.Sql(db => TwoWaySql(sql,
        bonus,
        Where(
            Condition(minCondition, 3000 < db.tbl_remuneration.money) &&
            Condition(maxCondition, db.tbl_remuneration.money < 4000))
        ));
    var info = query.Build(_connection.GetType());
    Debug.Print(info.SqlText);

    //Dapperを使っているなら以下で実行できます。
    //型はこの時点で指定してください
    var datas = _connection.Query<SelectedData>(query).ToList();
}
SELECT
    tbl_staff.name AS name,
    tbl_remuneration.payment_date AS payment_date,
    /*ここは置き換えられた*/
    tbl_remuneration.money + @bonus AS money
FROM tbl_remuneration 
    JOIN tbl_staff ON tbl_staff.id = tbl_remuneration.staff_id
/*ここも置き換えられた*/
/*最小の条件はfalseだったので消えた*/
/*条件がなくなった場合はWhereも消えます*/
WHERE (tbl_remuneration.money) < (@p_0)

Where句とかを動的に作って差し込めるのは便利ですね。まあ、好みで使い分けていただけたらと思います。それに、場合によってはラムダで書けないみたいなケースも出てきそうですし、そんな時にテキストも使えると心強いですよね!

履歴

2016/09/02 β版対応

LambdicSql - 断捨離 O/Rマッパー機能 捨てました。-

LambdicSqlもSQLを実行して、その結果をオブジェクトにマッピングする機能がありました。Dapperよりも便利なところもあって、匿名クラスにマッピングすることもできてました。ではなぜやめたか。
※Dapperでも匿名クラスにマッピングでる場合もあります。でも、失敗するケースもあり。DateTimeが入っていると大抵失敗する。詳細は追っていません。

//便利なとこもあるけど、この機能は捨てた
public void Test()
{
    //以前は匿名クラスにマッピングすることも可能だった。
    var query = Db<DB>.Sql<DB>().
    Select(db => new
    {
        name = db.tbl_staff.name,
        payment_date = db.tbl_remuneration.payment_date,
        money = db.tbl_remuneration.money,
    }).
    From(db => db.tbl_remuneration).
        Join(db => db.tbl_staff, db => db.tbl_remuneration.staff_id == db.tbl_staff.id);

    //読み取り専用なはずの匿名クラスにマッピング
    var data = query.ToExecutor(new SqlConnection(sqlConnectionString)).Read();
}

特徴を際立たせる

LambdicSqlはラムダからSQLを作成するためのライブラリです。DBにはアクセスしたり、マッピングする機能ははオマケだったのです。でも両方あると「新種のO/Rマッパーね」くらいに捉えられて特徴が伝わりづらいのではないかなーと。

Dapper or LambdicSqlだとDapper使うよね。

折角つくったのだから、広く多くの人に使ってもらいたいですよね。でもこの分野はEntityFrameworkやDapperがすでにあり、そんな信頼性の高いライブラリと張り合てもなかなかねー。総監督からも、「Dapperの利用者が二択せまられたら、Dapper使うよねー」とのご意見をいただきました。それはそうですね。

LambdicSql feat.Dapper

Hey yo yo yeah!オレたちDapperマジリスペクト!
ってわけでフィーチャリングします。実際DapperとLambdicSqlでは注力している部分が違うのです。LambdicSqlでSQLを組み立てて、実行はDapperでやってもらえばよいかなーって思います。ということでサンプルコードです。

public void TestStandard()
{
    var min = 3000;

    //LambdicSqlのお仕事
    //SQLを組み立てる
    var query = Db<DB>.Sql(db =>
        Select(new SelectData()
        {
            Name = db.tbl_staff.name,
            PaymentDate = db.tbl_remuneration.payment_date,
            Money = db.tbl_remuneration.money,
        }).
        From(db.tbl_remuneration).
            Join(db.tbl_staff, db.tbl_staff.id == db.tbl_remuneration.staff_id).
        Where(min < db.tbl_remuneration.money && db.tbl_remuneration.money < 4000));

    //テキストとパラメータに落とす
    var info = Db<DB>.Sql(_connection.GetType());
    Debug.Print(info.SqlText);

    //ここから先はDapperにお任せ
    //パラメータはDictionary<string, object>で渡す
    var datas = _connection.Query<SelectData1>(query).ToList();
}

というわけでDapper使いの皆さま、合わせてLambdicSqlのご利用もお願いします!