バッチ処理を50倍高速化する
DBデータをストリーム処理するを応用するとバッチ処理を高速化できます。SQL Serverに1億件のテストデータを高速に作成するの結果からすると、ストアドプロシージャで作成する場合に比べて50倍高速に処理できることになります。
メインフレーム上の基幹系のバッチの多くはファイルを読み込み、加工処理、出力する処理を繰り返して行います。これをRDBMSでファイルの代わりにテーブルを利用するイメージになります。
さらに、LINQ対応することでLINQで用意されたいろいろな仕組みを利用することができます。以下はAdventureWorksデータベースのSalesOrderDetailを顧客単位に集約して値引きを計算する処理です。AsParallelで並列化(スケールアウト)も簡単にできます。12万超のデータを処理するのに手元のPCで7.8秒です。100万件でも1分ちょいの処理時間ということになります。
var sw = new Stopwatch(); sw.Start(); using (var cnct = new SqlConnection(config.ConnectionString)) { cnct.Open(); var cmd = cnct.CreateCommand(); cmd.CommandText = @"SELECT * FROM Sales.SalesOrderDetail INNER JOIN Sales.SalesOrderHeader ON Sales.SalesOrderDetail.SalesOrderID = Sales.SalesOrderHeader.SalesOrderID Order by CustomerID, OrderDate"; cmd.ExecuteReader().AsEnumerable<SalesOrderDetailJoinSalesOrderHeader>() .GroupByCustomer() //顧客単位でまとめる .AsParallel() //並列化 .SelectMany(x=>CalcDiscount(x)) //値引き額を計算 .BulkCopy("[UpdateData]"); } sw.Stop(); Console.WriteLine("ElapsedMilliseconds:{0}", sw.ElapsedMilliseconds); Console.ReadLine();
LINQを使う場合の注意として、Linq to ObjectのGroup byなど全てデータを読み出してしまうような処理は避ける必要があります。メモリが爆発してしまいます。Group byのような処理は以下のようにソートしたデータを自前でグルーピングすればOKです。
public static IEnumerable<List<SalesOrderDetailJoinSalesOrderHeader>> GroupByCustomer( this IEnumerable<SalesOrderDetailJoinSalesOrderHeader> source) { SalesOrderDetailJoinSalesOrderHeader predata = null; var list = new List<SalesOrderDetailJoinSalesOrderHeader>(); foreach (var item in source) { if (predata != null && predata.CustomerID != item.CustomerID) { yield return list; list = new List<SalesOrderDetailJoinSalesOrderHeader>(); } list.Add(item); predata = item; } if (list.Count > 0) yield return list; } static List<UpdateData> CalcDiscount(List<SalesOrderDetailJoinSalesOrderHeader> source) { var total = source.Sum(x => x.LineTotal); var discount = total > 100 ? 1 : 0; var list = new List<UpdateData>(); foreach (var data in source) { count++; var item = new UpdateData(); item.SalesOrderID = data.SalesOrderID; item.SalesOrderDetailID = data.SalesOrderDetailID; item.UnitPriceDiscount = data.UnitPriceDiscount + discount; list.Add(item); } return list; }