I have a huge problem with Sql Server query execution time which I've debugged for a long time with no success.
Basically I'm generating a report where 'Order' statistics are grouped by order and shown to the user. The problem is, that most of the time the query execution is reasonably fast, but occasionally it plummets and causes timeouts on the server.
What I've gotten myself from it is that the occasional poor query performance seems to be caused by parameter sniffing in the SQL Server. My relations have very mixed amount of related rows; some relation might have 10 000 rows for one parent row but the next row could have only 1 related row. I think this causes the Query optimizer in some cases to ignore indexes completely and cause really poor performance.
Basically I have no idea how to approach with a fix to this problem. I either have to optimize my query below somehow OR come up with some way to force the query optimizer to use indexes every time. Stored procedures are not an option in this project, unfortunately.
What I've tried is to create independent requests for every 'Order', but as theres over 1000 orders in the system, that causes massive slowness and really isn't an option. The closest I've gotten to get it to run within a reasonable execution time is the query below which in turn seems to suffer from the parameter sniffing problem.
result = (from ord in db.Orders
join comm in db.Comments.Where(i =>
i.UserId == userId &&
i.Created >= startDate &&
i.Created < endDate &&
i.UserGroupId == channelId &&
i.Campaign.CampaignCountryId == countryId &&
(i.CommentStatus.Name == CommentStatus.Approved || i.CommentStatus.Name == CommentStatus.Pending))
on ord.OrderId equals comm.OrderId into Comments
join motif in db.Motifs.Where(i =>
i.UserId == userId &&
i.Created > startDate &&
i.Created < endDate &&
i.UserGroupId == channelId && i.Campaign.CampaignCountryId == countryId)
on ord.OrderId equals motif.OrderId into Motifs
where ord.EndDate > startDate
select new ReportRow()
{
OrderName = ord.Name,
OrderId = ord.OrderId,
ChannelId = channelId,
Comments = Comments.Count(c => c.CommentStatus.Name == CommentStatus.Approved),
PendingComments = Comments.Count(c => c.CommentStatu.Name == CommentStatus.Pending),
Motifs = Motifs.Count(),
UniqueMotifs = Motifs.GroupBy(c => c.Uin).Count(),
ApprovedValue = ((decimal?)Motifs.GroupBy(c => c.Uin).Select(c => c.FirstOrDefault()).Sum(c => c.Value) ?? 0) + ((decimal?)Comments.Where(c => c.Commentstatu.Name == Commentstatus.Approved).Sum(c => c.Value) ?? 0),
PendingValue = ((decimal?)Comments.Where(c => c.Commentstatu.Name == Commentstatus.Pending).Sum(c => c.Value) ?? 0)
}).ToList();
return result;
Any help and ideas about how to make my reporting run reasonably fast every time would be greatly appreciated - no matter if it's query optimizing itself or some awesome ideas for reporting in SQL in general.
I'm using Azure SQL if that makes any difference.
Also note that when I'm running the generated query from the LINQ above in SSMS I get good query execution time on every single run so the DB design shouldn't be a problem here albeit it might not be the most efficient solution anyway.