2

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.

veturi
  • 1,971
  • 2
  • 13
  • 17
  • maybe not an answer for your, just a thought. You could create a view for your report, and then query the view to get your results. This would ensure that the query runs fine in SQL every time, from what you are saying. – Adrian Nasui Dec 13 '14 at 12:23
  • Hey, thanks for your comment! Haven't really tried to create views. Could you point me to a right direction to get started with them? – veturi Dec 13 '14 at 12:37
  • I've posted an answer, you can ask for details there. – Adrian Nasui Dec 13 '14 at 12:40
  • Have you used a tool like linqpad to view the actual query that it generates? That might help you understand how EF is generating the sql and then in turn how sql server is building it's execution plan. Maybe your indexes need a little tweaking. – Brian Dishaw Dec 13 '14 at 13:08
  • I have checked the actual query done by EF yeah. It has plenty of joins for sure, but indexes should be properly set to every field the query is using for searching for. I've also tested few of the slow running queries by executing the actual SQL generated by EF in the Sql Server Management Studio where it every time runs just as it should - fast. That makes me think it is caused by the parameter sniffing issue. – veturi Dec 13 '14 at 13:16

1 Answers1

0

Maybe not an answer for your, just a thought. You could create a view for your report, and then query the view to get your results. This would ensure that the query runs fine in SQL every time, from what you are saying.

You use them similar to tables, and can make any queries on them.

Check this post for some tips on consuming views in EF.

Community
  • 1
  • 1
Adrian Nasui
  • 1,054
  • 9
  • 10
  • Thank you for your answer. Unfortunately view does not solve my problem as I need to do the query based on some variables - you can see them in my original SQL LINQ code. Also just as with stored procedures, views aren't acceptable solution in this project. – veturi Dec 14 '14 at 09:38