仕事でプログラミングやってた時に少し気になったので、実験してみた。
複数のテーブルを結合して一括でデータを取得するのと、各テーブルのデータを取得した後、プログラム側でデータを成型することのどっちが速いか。
1. 結論
EntityFrameworkで結合したほうが(かなり)早かった。
2. 環境
IDE:Visual Studio 2019
DB:Microsoft SQL Server 2017
3. やったこと
以下のような処理の速度を比較。
1つめ。テーブルからデータをガバっと取り、プログラム側で外部結合っぽい処理をする。
class Program
{
static void Main(string[] args)
{
var context = new TestContext();
var sw = new Stopwatch();
var _studentReposiroty = new StudentsRepository(context);
var _classReposiroty = new ClassesRepository(context);
var _clabReposiroty = new ClabsRepository(context);
var _userReposiroty = new UsersRepository(context);
var viewList = new List<TestViewModel>();
// プログラムでデータ成型
sw.Start();
// 特定の生徒データ取得
var students = _studentReposiroty.List();
// クラブとかのIdを取得
var clabIds = students.Select(x => x.ClabId).ToList();
var classIds = students.Select(x => x.ClassId).ToList();
var userIds = students.Select(x => x.CreatedByUserId).ToList();
// IDに紐づく名称のリストを(ID, Name)のタプルで取得
var classes = _classReposiroty.ListNameById(classIds);
var clabs = _clabReposiroty.ListNameById(clabIds);
var users = _userReposiroty.ListNameById(userIds);
// データ内にあるIDをNameに置き換える。対応するものがなかったら空文字。
students.ForEach(r =>
{
var className = !string.IsNullOrEmpty(classes.Find(x => x.Item1.Equals(r.ClassId)).Item2)
? classes.Find(x => x.Item1.Equals(r.ClassId)).Item2 : string.Empty;
var clabName = !string.IsNullOrEmpty(clabs.Find(x => x.Item1.Equals(r.ClabId)).Item2)
? clabs.Find(x => x.Item1.Equals(r.ClabId)).Item2 : string.Empty;
var userName = !string.IsNullOrEmpty(users.Find(x => x.Item1.Equals(r.CreatedByUserId)).Item2)
? users.Find(x => x.Item1.Equals(r.CreatedByUserId)).Item2 : string.Empty;
var testViewModel = new TestViewModel
{
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();
var viewList2 = _studentReposiroty.GetTestViewModels();
sw.Stop();
public List<TestViewModel> GetTestViewModels()
{
var returnList = _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) => new TestViewModel
{
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();
return returnList;
}
4.測定結果
前者:4189ミリ秒
後者:160ミリ秒
というわけで、後者のほうが全然早かった。。。。
ListじゃなくてHashSet使うともっと違うのかしら。
でもまあ、ミリ秒の世界だったら、正直HashSetでもっと早くなったところでどっちでもいいかなぁ。。。。