こんにちは。
先日BulkCopyクラスを使うにあたって、
・DataTableを使うべきか?
・IDataReaderを実装したクラスを用意するべきか?
ということで学びがありました。
そこで、
「DataTable を使わずに IDataReader を使うことをお勧めします。」
「DataTable だとメモリ使うわパフォーマンス悪いわでいい事ないです。」
ということが実感できるサンプルを作ったので見ていってください。
《仕様》
Step0: 何人かの「かわい」と何杯かの「ビール」があるものとする
Step1: すべての「かわい」とすべての「ビール」を組み合わせた「かわいとビール」データを作成する
Step2: 作成した「かわいとビール」の大量データをBulkCopy.WriteToServer()でDBにつっこむ
今回は元の2つのリストからBulkCopyするための大量データを作成するにあたって、
「DataTableを使う場合」と「IDataReaderを実装したクラスを使う場合」を比較し、
いかに「DataTableがDestroyするに値するものであるか」ということを実感していただきます。
◆イメージ図:2人の「かわい」と3杯の「ビール」の例
※「かわい」と「ビール」のリストは、Step0にてあらかじめ用意されているものとします。
以下のようなサンプルコードで実験です。
※とりあえず「かわい100人」と「ビール100杯」で実験
※3つの自作クラスを使用 ⇒ 各自作クラスの中身は後述
①DataRepositoryクラス
…Step0において「かわい」と「ビール」のリストを生成するクラス
②KawaiAndBeerDataReaderクラス
…DBにBulkCopyする大量データを用意するIDataReaderを実装したクラス
③KawaiAndBeerDataTableクラス
…DBにBulkCopyする大量データをDataTableとして用意するクラス
static void Main(string[] args) { var connectionString = "Data Source = *;Initial Catalog = *;User ID = *;Password = *"; var sw1 = new Stopwatch(); var sw2 = new Stopwatch(); //★Step0: あらかじめ「100人のかわい」と「100杯のビール」は用意されているとする var kawais = DataRepository.GetKawais(100); var beers = DataRepository.GetBeers(100); //★Step1: 100*100 = 10000件の大量データを作成する sw1 = Stopwatch.StartNew(); var reader = new KawaiAndBeerDataReader(kawais, beers);//←IDataReaderの場合 //var table = new KawaiAndBeerDataTable(kawais, beers);//←DataTableの場合 sw1.Stop(); //★Step2: 大量データをBulkCopy.WriteToServer() sw2 = Stopwatch.StartNew(); using (var bc = new SqlBulkCopy(connectionString) { DestinationTableName = "BulkCopyTestTable", }) { bc.WriteToServer(reader);//←IDataReaderの場合 //bc.WriteToServer(table);//←DataTableの場合 } sw2.Stop(); //結果出力 Console.WriteLine("DataReader:");//←IDataReaderの場合 //Console.WriteLine("DataTable:");//←DataTableの場合 Console.WriteLine("Step1: " + sw1.Elapsed); Console.WriteLine("Step2: " + sw2.Elapsed); Console.ReadLine(); } |
---|
以下、サンプルコードです:
「パフォーマンスってまぁ気にしなきゃいけないと思うけど、実際どれほどの差がでるわけ?」
と思ってPGしてきましたが、今後は生まれ変わった気持ちでソースと向き合うことができそうです。
実装ができて半人前、パフォーマンスや可読性・保守性の高いコードが書けて一人前!
今後も皆様からのツッコミ、お待ちしてます!