Caverman
Caverman

Reputation: 3739

How to convert Group By SQL to LINQ?

I have the following SQL statement I'm trying to convert to Entity Framework.

SELECT S_NUMBER,A_NUMBER,FIRST_NAME,LAST_NAME
FROM EMPLOYEE WHERE S_NUMBER IN (
        SELECT S_NUMBER
        FROM EMPLOYEE 
        WHERE CO='ABC'
        GROUP BY S_NUMBER
        HAVING COUNT(*) > 1)

I've done some searching on using Group By in LINQ as well as sub-queries. I'm using LinqPad with a "C# Statement" and I came up with the following which based on some examples I found looks like it should work. However, I'm getting errors when trying to assign esn.S_NUMBER to sNumber in the anonymous object. The message says 'IGrouping' does not contain a definition for 'S_NUMBER'.

 var result = from e in EMPLOYEE    
              where e.CO=="ABC" 
              group e by e.S_NUMBER into esn      
              select new
              {
                  sNumber = esn.S_NUMBER
              };


result.Dump();

I was under the impression that all the records would basically get put into a temp table called esn and I could be able to call the temptable.column name to assign it to my object that I will eventually return as a list.

Upvotes: 2

Views: 521

Answers (2)

Corey
Corey

Reputation: 16584

Since you're using the results of one query to filter another we can do a fairly direct transliteration of the query like so:

var result =
    from e in EMPLOYEE
    join f in (
        from fe in EMPLOYEE
        where fe.CO == 'ABC'
        group null by S_NUMBER into grp
        where grp.Count() > 1
        select grp.Key
    )
    on e.S_NUMBER equals f
    select new { e.S_NUMBER, e.A_NUMBER, e.FIRST_NAME, e.LAST_NAME };

Not only does this look a lot more like the original query but it should perform a bit faster (on MS SQL at least, can't speak for others) than the other form that might be simpler in LINQ but is much more complex when converted to SQL... four selects and a cross join, in my test version, vs two selects and an inner join for this one.

Of course if you prefer you can pull the inner query out as a separate IQueryable for clarity:

var filter = 
    from e in EMPLOYEE
    where e.CO == 'ABC'
    group null by S_NUMBER into grp
    where grp.Count() > 1
    select grp.Key;

var result =
    from e in EMPLOYEE
    join f in filter
    on e.S_NUMBER equals f
    select new { e.S_NUMBER, e.A_NUMBER, e.FIRST_NAME, e.LAST_NAME };

Upvotes: 1

Dave Zych
Dave Zych

Reputation: 21897

You want to use Key instead of S_NUMBER. When grouping, the results get put into a IEnumerable<IGrouping>>. The grouping has a Key property which holds the key for that group, which in this case it's your S_NUMBER.

select new
{
    sNumber = esn.Key
};

The following query should be a translation of the original SQL query. Instead of using a subquery, we're grouping and doing another from...in to "flatten" the sequence, and also checking that each grouping has a count > 1 like the original query.

var result = from e in EMPLOYEE
             where e.CO=="ABC"
             group e by e.S_NUMBER into esn
             from e2 in esn
             where esn.Count() > 1
             select new
             {
                 e.S_NUMBER,
                 e.A_NUMBER,
                 e.FIRST_NAME,
                 e.LAST_NAME
             };

Upvotes: 4

Related Questions