I want to know if this query I wrote for statistics is fast. It currently works in 3 seconds with the data and query I wrote below.
Now I have a user table. Those whose role is Hr can create a vacancy. So the user has a vacancy list. Users whose role is candidate can apply to these vacancies and this is stored in the applications table. So there is a list of applications in vacancy. Now I need statistics like this:
- Names of HR users
- Total shared vacancy numbers of HR users
- Total number of completed vacancies of HR users
- Number of applications that have reached "CV approved" status in HR users’ vacancies
- Number of applications that have reached "Interview" status in the vacancies of HR users
- Number of applications that have reached "Exam" status in the vacancies of HR users
- And +10 more than application status in this type
Row numbers in my database:
- All users: +125 000 (55 Hr users)
- Vacancies: 1900
-Applications: +340 000
My query is as follows:
var statisticsQuery = _context.Users
.OrderBy(hrUser => hrUser.Id)
.Where(hrUser => hrUser.Roles.Any(p => p.RoleId == "Hr"))
.Select(hrUser => new VacancyByRecruiterModelDto
{
RecruiterId = hrUser.Id,
RecruiterFullName = hrUser.Name + " " + hrUser.Surname,
PublishedTotalVacancyCount = hrUser.Vacancies.Count(vac => vac.StatusId == 1),
CompletedTotalVacancyCount = hrUser.Vacancies.Count(vac => vac.StatusId == 2),
CvCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 1),
ExamCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 2),
InterviewCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 3),
DocumentCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 4),
HiringSuccessCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 5),
CvFailedCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 6),
CandidateFailedCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 7),
DontSeeCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 8),
ReserveCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 9),
InternCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 10),
SecurityCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 11),
DontExamCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 12),
DontInterviewCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 13),
HiringStoppedCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 14),
FailInternshipCount = hrUser.Vacancies
.SelectMany(vac => vac.Applications)
.Count(app => app.StatusId == 15),
}).AsNoTracking();
if (loadMore?.Skip != null && loadMore?.Take != null)
{
statisticsQuery = statisticsQuery.Skip(loadMore.Skip.Value).Take(loadMore.Take.Value);
}
Now, when I set Take 10 and query only for 10 Hr users, the above code runs in 3 seconds. Is this normal or should it be further optimized? If it should be done, how?
2
Answers
You can consider updating the DTO to store the application counts as a grouping to be filled in by EF/Linq then expose helper properties for the specific counts:
Then when populating:
Similarly, this can be done for the vacancy counts if there is a chance that there could be more than two statuses. Note that
AsNoTracking
is only needed when you are loading entities. It is not necessary/used when projecting.Try the following optimization. I have used grouping to count statuses separately and then joined the result with the users. It should reduce count of subqueries and improve performance.