浮動小数点数の演算誤差について
floatやdoubleの演算誤差についてよくまとまっている。いきなりIEEE754とか。
PropertyInfo, FieldInfoの内部キャッシュ
式木を使っていてふと思ったが Expression
, PropertyInfo
, FieldInfo
あたり(どれかはよくわからないが)は内部的にキャッシュされている気がする。初回実行時と2回目以降実行時で1000倍以上Performanceに差がでる。
Pattern 1
これが、ローカルで 200μs
var sss = new MySqlLam<Employee>(e => e.Id >= id) .In(e => e.FirstName, list) .And(e => e.LastName != list[0]) .OrderBy(e => e.Id, true) .OrderBy(e => e.FirstName); Stopwatch sw = new Stopwatch(); sw.Start(); var sss2 = new MySqlLam<Employee>(e => e.LastName == "aaa"); sw.Stop(); Console.WriteLine("経過時間の合計 = {0}", sw.Elapsed);
尚、計測するクエリを sss
と同じくらい複雑にしてもほぼ影響なし
Pattern 2
これが、ローカルで 300ms (1500倍遅い)
//var sss = new MySqlLam<Employee>(e => e.Id >= id) // .In(e => e.FirstName, list) // .And(e => e.LastName != list[0]) // .OrderBy(e => e.Id, true) // .OrderBy(e => e.FirstName); Stopwatch sw = new Stopwatch(); sw.Start(); var sss2 = new MySqlLam<Employee>(e => e.LastName == "aaa"); sw.Stop(); Console.WriteLine("経過時間の合計 = {0}", sw.Elapsed);
所感
- 最適化されるとはいえ、それでも 200μs は早くはないので要求されるPerformanceと相談
WIP, C#, 式木という黒魔術でラムダ式からSQLクエリ文字列を生成する
生放送で使ったもの
サイト
www.slideshare.net
コード
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; // BinaryExpression(二項演算), UnaryExpression(単項式), ConstantExpression(定数) namespace ExpressionTree { class Program { static void Main(string[] args) { // "WHERE - IN" の書き方例 // ServiceStack.OrmLite: q => Sql.In(q.City, "London", "Madrid", "Berlin") // lambda-sql-builder: WhereIsIn(c => c.CategoryName, new object[] { "Beverages", "Condiments" }) // "WHERE id = 1 and first_name = John", "WHERE id > 10" Expression<Func<Employee, bool>> exp = (e) => e.Id > 123 && e.FirstName == "John" && e.LastName == "Abc" && e.Id == 456; var visitor = new ParameterVisitor(); visitor.Visit(exp.Body); } } /// <summary> /// 式木をparseするためのクラス /// 参考:http://qiita.com/takeshik/items/438b845154c6c21fcba5 /// </summary> public class ParameterVisitor : ExpressionVisitor { public ParameterVisitor() { } protected override Expression VisitBinary(BinaryExpression node) { // ~~可変個の条件式が渡ってくるので、再帰的にパースできるような実装をする~~ → 下記のようにメソッドチェインの制約を付与すれば楽か // lambda-sql-builder: new SqlLam<Employee>(p => p.Id > 1).And(p => p.Title != "Sales Representative") return null; } protected override Expression VisitParameter(ParameterExpression node) { return null; } protected override Expression VisitUnary(UnaryExpression node) { return null; } } /// <summary> /// SQL Builder Library. WHERE句以降のクエリだけを出力対象にすることで小さく保つ予定 /// </summary> public class SqlLam<T> where T : IOrm { // Visitor pattern ParameterVisitor visitor = new ParameterVisitor(); // WHERE句以降のクエリ文字列 public string Query { get; private set; } // TODO: コンストラクタでもWHERE条件を受け付けるかも public SqlLam(Expression<Func<T, bool>> expression) { } public SqlLam<T> And(Expression<Func<T, bool>> expression) { visitor.Visit(expression.Body); return this; } // TODO: WHERE ~ IN public SqlLam<T> In(Expression<Func<T, object>> field, object values) { return this; } // TODO: ORDER BY public SqlLam<T> OrderBy(Expression<Func<T, object>> field, bool descending = false) { return this; } // TODO: LIMIT public SqlLam<T> Limit(int number) { return this; } // TODO: number public SqlLam<T> Offset(int number) { return this; } } public class Employee : IOrm { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } public interface IOrm { } }
第9回 良いSQLについて
WHERE - IN
は使わざるを得ない場合も多いと思うが、OR
は避けられる。
索引を使えないケース
- NULL比較やNOT(!=)を使用
- 列を演算している
- 後方一致(中間一致)条件
- INリストまたはORを使用