0

I've asked this question before and got down-voted because I didn't provide code. But I strongly believe my problem is not code related and I am very desperate to solve this problem.

Basically, I need to get counts of 2 different things from a LINQ query, the query is being built up and is pretty long.

The weird thing is: when the select LINQ query is executed on the database server, it times out on the 2nd count (no matter how long I set the CommandTimeout to), but when I grabbed the same database instance from the database server and put it on my local then execute the LINQ query it gets both counts instantly.

I SQL Profiler the translated SQL query on both the database server and the local server and they are the same. When translated SQL query that's generated from my local is ran on the database server's SQL Management Studio manually, it also returns the count instantly.

This is why I highly suspect it is not a code issue or N+1 issues like many have suggested. I suspect its an environment issue, but that's beyond my knowledge why this is happening. If anyone has ran into this before, can someone provide an opinion? If you need LINQ code please let me know I can shorten it and upload.

        IQueryable<StudentDm> studentQuery = GetListQuery();
        IQueryable<StudentTestDm> studentTestQuery = studentQuery.SelectMany(a => a.StudentTests);

        if (studentAcademicTestProficiencyGrowthParameter.TestTypeId.HasValue)
        {
            studentTestQuery = studentTestQuery.Where(a => a.TestTypeId == studentAcademicTestProficiencyGrowthParameter.TestTypeId);
        }

        DateTime serviceDateFrom = (studentAcademicTestProficiencyGrowthParameter.ServiceDateFrom.HasValue) ? studentAcademicTestProficiencyGrowthParameter.ServiceDateFrom.Value : DateTime.MinValue;
        DateTime serviceDateTo = (studentAcademicTestProficiencyGrowthParameter.ServiceDateTo.HasValue) ? studentAcademicTestProficiencyGrowthParameter.ServiceDateTo.Value : DateTime.MaxValue;

        // test 1 query
        IQueryable<StudentTestDm> studentTest1Query = studentTestQuery;

        if (studentAcademicTestProficiencyGrowthParameter.Test1SchoolYear.HasValue)
        {
            studentTest1Query = studentTest1Query.Where(a => a.Year == studentAcademicTestProficiencyGrowthParameter.Test1SchoolYear);
        }

        if (studentAcademicTestProficiencyGrowthParameter.Test1TestDate.HasValue)
        {
            studentTest1Query = studentTest1Query.Where(a => a.TestDate == studentAcademicTestProficiencyGrowthParameter.Test1TestDate);
        }

        // we grab the grouped students who has non-proficient test, and who does not have proficient test
        IQueryable<IGrouping<StudentDm, StudentTestDm>> groupedStudentWithTest1 = studentTest1Query.GroupBy(a => a.Student).Where(a => a.Select(b => b.TestLevel.ProficiencyId).Contains((int)LookUpEnum.LookUpEnum.TestLevelProficiency.NotProficient)
                                                                                                                                    && !a.Select(b => b.TestLevel.ProficiencyId).Contains((int)LookUpEnum.LookUpEnum.TestLevelProficiency.Proficient));

        // get the count of students whos attended less than 30 days of services
        count1 = groupedStudentWithTest1.Where(a => a.Key.StudentServices.Where(b => b.Date >= serviceDateFrom && b.Date <= serviceDateTo).GroupBy(b => b.Date).Count() < 30).Count();

        // test 2 query
        IQueryable<StudentTestDm> studentTest2Query = studentTestQuery;

        if (studentAcademicTestProficiencyGrowthParameter.Test2SchoolYear.HasValue)
        {
            studentTest2Query = studentTest2Query.Where(a => a.Year == studentAcademicTestProficiencyGrowthParameter.Test2SchoolYear);
        }

        if (studentAcademicTestProficiencyGrowthParameter.Test2TestDate.HasValue)
        {
            studentTest2Query = studentTest2Query.Where(a => a.TestDate == studentAcademicTestProficiencyGrowthParameter.Test2TestDate);
        }

        // we are grabbing the students who are not-proficient in test 1 but are proficient in test 2
        IQueryable<IGrouping<StudentDm, StudentTestDm>> groupedStudentWithImprovedTest2 = studentTest2Query.GroupBy(a => a.Student).Where(a => a.Select(b => b.TestLevel.ProficiencyId).Contains((int)LookUpEnum.LookUpEnum.TestLevelProficiency.Proficient))
                                                                                                                                   .Where(a => groupedStudentWithTest1.Select(b => b.Key.Id).Contains(a.Key.Id)); 

        // THIS QUERY TIMES OUT (ON Database Server): get the count students who attended less than 30 days of services
        count2 = groupedStudentWithImprovedTest2.Where(a => a.Key.StudentServices.Where(b => b.Date >= serviceDateFrom && b.Date <= serviceDateTo).GroupBy(b => b.Date).Count() < 30).Count();
andyh0316
  • 355
  • 2
  • 17
  • Check whether the connection with the server is proper with your database – Praburaj Jan 02 '17 at 09:57
  • 1
    @Prabu that comment makes no sense. Explain what you mean by _"proper with your database"_. – CodeCaster Jan 02 '17 at 09:57
  • 1
    So what is your expectation? That somebody comes up not knowing your code or database or environment and presents a solution? – nvoigt Jan 02 '17 at 09:57
  • 1
    @nvoigt I don't EXPECT anything. I hope that someone who has ran into this exact situation before can provide some hint as to which direct I could go to start debugging. I already described my problem as much as I can. If you still strongly believe that I need to upload my LINQ code then you can just say that. – andyh0316 Jan 02 '17 at 09:59
  • 1
    @CodeCaster andy gets timeout error. It might be because the connection would not been established.. – Praburaj Jan 02 '17 at 10:00
  • @Prabu I'm sorry, but again, that makes very little sense. Don't throw uneducated guesses at the OP, this will only confuse them more. – CodeCaster Jan 02 '17 at 10:00
  • @CodeCaster You can take a look at this page https://technet.microsoft.com/en-us/library/ms190181(v=sql.105).aspx – Praburaj Jan 02 '17 at 10:01
  • @Prabu Thanks for the answer. But I can make sure that the connection is established because I can get counts for other things in the same query. But it timesout on this specific count. When I suspected that It was query not optimized I extended the timeout and nothing. What surprised me was that when I ran the very same Linq Query on my local it is instant. – andyh0316 Jan 02 '17 at 10:01
  • @andyh0316 If you feel your query is not optimized then you can post the code here which might help others to either optimize or find problem with it – Praburaj Jan 02 '17 at 10:03
  • @Prabu No, I dont think the code is not optimized because it executed in less than 1 second on my local environment (whether if its by LINQ query or by translated SQL query manually) – andyh0316 Jan 02 '17 at 10:04
  • @andy the LINQ part is irrelevant, the only thing that matters is the SQL and the execution plan. – CodeCaster Jan 02 '17 at 10:04
  • @CodeCaster so what information do you think would be helpful for me to give here? – andyh0316 Jan 02 '17 at 10:05
  • @andyh0316 it might because of the large amount of data in your server database compared to your local database – Praburaj Jan 02 '17 at 10:05
  • @Pradu, I copied the server database onto my local. I tried to duplicate as much as possible onto my local (code, database, SQL Profiler code, etc). – andyh0316 Jan 02 '17 at 10:07
  • @andy unless you post your code no one could help you as the problem is not so clear – Praburaj Jan 02 '17 at 10:08
  • Posted code .. if its too much noise let me know I will shorten it more – andyh0316 Jan 02 '17 at 10:15
  • I commented in your prior question (that I believe you've deleted rather than enhanced) but I got no response. Are you any more likely to respond this time? – Nick.Mc Jan 02 '17 at 10:42
  • @Nick.McDermaid I'm sorry what question was it? – andyh0316 Jan 02 '17 at 10:52
  • hmmm sorry I can't find it in my history but there was a LINQ performance question where someone suggested it was a N+1 problem.. maybe it was you maybe it wasn't. Anyway... I have two suggestion for starters (both database side): 1. In SSMS, use activity monitor to confirm there aren't any deadlocks (and in fact to determine if the database work is finished or not); 2. When capturing the query in SQL Profiler, make sure you include all of the preceding `SET` options also (i.e. `SET ARITHABORT OFF`) when reproducing in SSMS. – Nick.Mc Jan 02 '17 at 11:01
  • Below are two links that helped me solve my LINQ performance issue which turned out to be parameter sniffing. The weird thing was that running it in SSMS was fine..... until I reproduced it _properly_ with the _full_ query, which *includes* all the set options. Then I saw the performance issue in SSMS and could troubleshoot further. http://www.sommarskog.se/query-plan-mysteries.html http://dba.stackexchange.com/questions/9840/why-would-set-arithabort-on-dramatically-speed-up-a-query – Nick.Mc Jan 02 '17 at 11:03
  • @Nick.McDermaid Thank you for suggestion. Deadlock sounds pretty reasonable. I am going to learn how to use activity monitor to start debugging. – andyh0316 Jan 02 '17 at 11:05
  • A vast improvement on activity monitor, when your'e ready, is a stored procedure called `sp_whoisactive`. Anyway it'll be interesting to find out what you see. – Nick.Mc Jan 02 '17 at 11:09
  • Also if it's OK it would be good if you post the query that you captured. – Nick.Mc Jan 02 '17 at 11:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/132086/discussion-between-andyh0316-and-nick-mcdermaid). – andyh0316 Jan 02 '17 at 11:23

1 Answers1

1

See chat discussion. The query followed the 'optional filtering' pattern, and the issue was parameter sniffing.

The query was captured from SQL Profiler and run in SSMS but the issue could not be reproduced.

However when all the preceding SET options were also added from SQL Profiler, the problem was reproduced in SSMS

Adding OPTION (RECOMPILE) to the end of the query appeared to solve the issue.

(Specifically this was added right before the closing quote in the sp_executesql)

This link EF 6 Parameter Sniffing shows a way to add OPTION (RECOMPILE) via LINQ. I will await a reponse from the original poster to ascertain whether this really works or not.

The link says "It's possible to use the interception feature of EF6"

And shows some code like this (abridged):

public class OptionRecompileHintDbCommandInterceptor : IDbCommandInterceptor
{


    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        addQueryHint(command);
    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        addQueryHint(command);
    }

    private static void addQueryHint(IDbCommand command)
    {
        if (command.CommandType != CommandType.Text || !(command is SqlCommand))
            return;

        if (command.CommandText.StartsWith("select", StringComparison.OrdinalIgnoreCase) && !command.CommandText.Contains("option(recompile)"))
        {
            command.CommandText = command.CommandText + " option(recompile)";
        }
    }
}
Community
  • 1
  • 1
Nick.Mc
  • 18,304
  • 6
  • 61
  • 91