仕事でプログラミングやってた時に少し気になったので、実験してみた。
複数のテーブルを結合して一括でデータを取得するのと、各テーブルのデータを取得した後、プログラム側でデータを成型することのどっちが速いか。
1. 結論
EntityFrameworkで結合したほうが(かなり)早かった。
2. 環境
IDE:Visual Studio 2019
DB:Microsoft SQL Server 2017
3. やったこと
以下のような処理の速度を比較。
1つめ。テーブルからデータをガバっと取り、プログラム側で外部結合っぽい処理をする。
classProgram{staticvoidMain(string[]args){varcontext=newTestContext();varsw=newStopwatch();var_studentReposiroty=newStudentsRepository(context);var_classReposiroty=newClassesRepository(context);var_clabReposiroty=newClabsRepository(context);var_userReposiroty=newUsersRepository(context);varviewList=newList<TestViewModel>();// プログラムでデータ成型sw.Start();// 特定の生徒データ取得varstudents=_studentReposiroty.List();// クラブとかのIdを取得varclabIds=students.Select(x=>x.ClabId).ToList();varclassIds=students.Select(x=>x.ClassId).ToList();varuserIds=students.Select(x=>x.CreatedByUserId).ToList();// IDに紐づく名称のリストを(ID, Name)のタプルで取得varclasses=_classReposiroty.ListNameById(classIds);varclabs=_clabReposiroty.ListNameById(clabIds);varusers=_userReposiroty.ListNameById(userIds);// データ内にあるIDをNameに置き換える。対応するものがなかったら空文字。students.ForEach(r=>{varclassName=!string.IsNullOrEmpty(classes.Find(x=>x.Item1.Equals(r.ClassId)).Item2)?classes.Find(x=>x.Item1.Equals(r.ClassId)).Item2:string.Empty;varclabName=!string.IsNullOrEmpty(clabs.Find(x=>x.Item1.Equals(r.ClabId)).Item2)?clabs.Find(x=>x.Item1.Equals(r.ClabId)).Item2:string.Empty;varuserName=!string.IsNullOrEmpty(users.Find(x=>x.Item1.Equals(r.CreatedByUserId)).Item2)?users.Find(x=>x.Item1.Equals(r.CreatedByUserId)).Item2:string.Empty;vartestViewModel=newTestViewModel{Id=r.Id,Name=r.Name,ClassName=className,ClabName=clabName,CreatedByUserName=userName,Age=r.Age};viewList.Add(testViewModel);});sw.Stop();Console.WriteLine($"処理時間: {sw.ElapsedMilliseconds}ミリ秒");Console.WriteLine();2つ目。LINQで一気にデータを取る。
sw.Restart();varviewList2=_studentReposiroty.GetTestViewModels();sw.Stop();publicList<TestViewModel>GetTestViewModels(){varreturnList=_testContext.Students.GroupJoin(_testContext.Classes,s=>s.ClassId,c=>c.Id,(s,c)=>new{s=s,c=c}).SelectMany(x=>x.c.DefaultIfEmpty(),(s,c)=>new{s=s.s,c}).GroupJoin(_testContext.Clabs,sc=>sc.s.ClabId,clab=>clab.Id,(sc,clab)=>new{sc,clab}).SelectMany(x=>x.clab.DefaultIfEmpty(),(sc,clab)=>new{sc=sc.sc,clab}).GroupJoin(_testContext.Users,scc=>scc.sc.s.CreatedByUserId,u=>u.Id,(scc,u)=>new{scc,u}).SelectMany(x=>x.u.DefaultIfEmpty(),(scc,u)=>newTestViewModel{Id=scc.scc.sc.s.Id,Name=scc.scc.sc.s.Name,ClabName=scc.scc.clab.Name??string.Empty,ClassName=scc.scc.sc.c.Name??string.Empty,CreatedByUserName=u.Name??string.Empty,Age=scc.scc.sc.s.Age}).ToList();returnreturnList;}4.測定結果
前者:4189ミリ秒
後者:160ミリ秒
というわけで、後者のほうが全然早かった。。。。
ListじゃなくてHashSet使うともっと違うのかしら。
でもまあ、ミリ秒の世界だったら、正直HashSetでもっと早くなったところでどっちでもいいかなぁ。。。。