BStill
BStill

Reputation: 923

How can I speed up my LINQ query without using a table twice?

So I am using a table twice in my query, and I dont know of another way to only use it once. Here is my query:

var result = (from t1 in (from t1 in db.Students.Where(en => en.Progress == MyEnum.Accepted).GroupBy(en => en.Class).AsEnumerable()
                          join t2 in dbOther.Classes on t1.Key equals t2.Class 
                          select t2)
              join t3 in (db.Students).AsEnumerable() on t1.Class equals t3.Class into t4
              select new
                          {
                              ClassNum = t1.Class,
                              StartDate = t1.StartDate,
                              Weeks = t1.Weeks,
                              Accepted = t4.Where(e => e.Progress == MyEnum.Accepted).Count(),
                              NotAccepted = t4.Where(e => e.Progress < MyEnum.Accepted).Count()
                          }).ToList();

I need to only get classes that have students in the accepted state. Then I want to get the classes and the count of its entire roster, even the students that are not accepted. Is there a better way to do this? It seems like reading from the same table twice is not the fastest way to do this.

Thank you for the help.

Upvotes: 0

Views: 111

Answers (3)

gnud
gnud

Reputation: 78598

Unless you're really simplifying the query here, you don't really need to hit dbOther to find out what classes has any students in the Accepted state. That information is in db.

var studentCountPerClass = (from s in db.Students
                            group s by s.Class into studentsByClass    
                            where studentsByClass.Any(x => x.Progress == MyEnum.Accepted)
                            select new
                            {
                                ClassNum = studentsByClass.Key,
                                Accepted = studentsByClass.Count(s => s.Progress == MyEnum.Accepted),
                                NotAccepted = studentsByClass.Count(s => s.Progress < MyEnum.Accepted),
                            })
                            .ToList();

var classCodes = studentCountPerClass.Select(x => x.ClassNum).ToList(); 
var classData = (from c in dbOther.Classes
                    where classCodes.Contains(c.Class)
                    select new {
                        c.Class,
                        // Any other data you want about the class
                    })

Upvotes: 1

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726929

First, fetch information about student enrollment from db.Students table:

var stats = db.Students
    .GroupBy(en => en.Class)
    .Select(g => new {
        Class = g.Key
    ,   Accepted = g.Count(en => en.Progress == MyEnum.Accepted)
    ,   NotAccepted = g.Count(en => en.Progress < MyEnum.Accepted)
    })
    .Where(g => g.Accepted != 0)
    .ToList();

Next, fetch classes from the other database:

var classIds = stats.Select(en => en.Class).ToList();
var classes = dbOther.Classes.Where(c => classIds.Contains(c.Class)).ToList();

Finally, join the two into the result:

var result = (from s in stats
    join c in classes on c.ClassId = s.ClassId
    select new {
        ClassNum = c.Class
    ,   c.StartDate
    ,   c.Weeks
    ,   s.Accepted
    ,   s.NotAccepted
    }).ToList();

Upvotes: 1

NitinSingh
NitinSingh

Reputation: 2078

Instead of USING ". Enumerable()" over the student db object, use Querable. This will create an expression which gets evaluated at database level rather than fetching entire student table first and then doing the comparison.

Check this for details Returning IEnumerable<T> vs. IQueryable<T>

Just to try, set the profiler and see the generated queries for both Enumerable vs Queryable.. The difference will get illustrated there.

Entity framework is easiest showcased with differences in execution with both these methods. Also the simplest ORM to support these functions.

Upvotes: 0

Related Questions