LINQ
left join
group by
count
C#

LINQ - Left Join, Group By, and Count

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In LINQ, "left join, group by, and count" usually means one thing: keep every row from the left side, even when the right side has no match, and report how many matches each left-side row has. The easiest solution is often a GroupJoin, because it already preserves the left side and gives you a sequence to count. Developers often make this harder than necessary by flattening too early.

Start with GroupJoin

Suppose you have customers and orders, and you want one output row per customer:

csharp
1using System;
2using System.Linq;
3
4var customers = new[]
5{
6    new { Id = 1, Name = "Alice" },
7    new { Id = 2, Name = "Bob" },
8    new { Id = 3, Name = "Cara" }
9};
10
11var orders = new[]
12{
13    new { Id = 10, CustomerId = 1 },
14    new { Id = 11, CustomerId = 1 },
15    new { Id = 12, CustomerId = 3 }
16};
17
18var result =
19    from c in customers
20    join o in orders on c.Id equals o.CustomerId into orderGroup
21    select new
22    {
23        c.Name,
24        OrderCount = orderGroup.Count()
25    };
26
27foreach (var row in result)
28{
29    Console.WriteLine($"{row.Name}: {row.OrderCount}");
30}

This already behaves like a left join for counting. Bob still appears, and OrderCount is 0.

That is the key insight: for aggregate results, a grouped join is often the final form you need. There is no requirement to flatten it first just because SQL syntax often starts from a joined row set.

Why This Counts Correctly

A GroupJoin gives each left-side row a collection of matching right-side rows. If there are no matches, the collection is empty rather than missing. That makes Count() reliable and readable.

The query shape is effectively:

  • one row from the left side
  • zero or more matching rows from the right side
  • an aggregate computed from that group

That maps perfectly to "customer with order count" and similar reporting tasks such as "department with employee count" or "blog post with comment count."

When You Actually Need a Flattened Left Join

Sometimes you do need one row per pair before grouping again. In that case, use DefaultIfEmpty() after the grouped join:

csharp
1var flattened =
2    from c in customers
3    join o in orders on c.Id equals o.CustomerId into orderGroup
4    from item in orderGroup.DefaultIfEmpty()
5    select new
6    {
7        c.Name,
8        OrderId = item?.Id
9    };

Now the result contains one row for each order, plus one placeholder row for customers with no orders.

If you later group this flattened data, count only real matches:

csharp
1var grouped =
2    from row in flattened
3    group row by row.Name into g
4    select new
5    {
6        Name = g.Key,
7        OrderCount = g.Count(x => x.OrderId != null)
8    };

This pattern is valid, but it is more verbose than the direct GroupJoin approach when the goal is only an aggregate count.

Method Syntax Version

Many codebases prefer fluent LINQ. The same logic in method syntax looks like this:

csharp
1var result = customers.GroupJoin(
2    orders,
3    customer => customer.Id,
4    order => order.CustomerId,
5    (customer, orderGroup) => new
6    {
7        customer.Name,
8        OrderCount = orderGroup.Count()
9    });

This version is especially useful when the query is built step by step or composed with helper methods.

LINQ to Objects Versus LINQ Providers

The ideas are the same across LINQ to Objects, Entity Framework, and similar providers, but translation matters. A simple GroupJoin with a count is generally easier for a provider to translate into SQL than a more complicated flatten-then-regroup pipeline.

If the query runs against a database, keep it as direct as possible:

  • prefer the grouped form when only a count is needed
  • avoid projecting large intermediate shapes without a reason
  • verify the generated SQL if performance matters

That keeps both the C# and the database query plan easier to understand.

A More Realistic Example

Here is a common reporting scenario with departments and employees:

csharp
1var summary =
2    from d in departments
3    join e in employees on d.Id equals e.DepartmentId into employeeGroup
4    select new
5    {
6        d.Name,
7        ActiveEmployees = employeeGroup.Count(emp => emp.IsActive)
8    };

This shows another advantage of the grouped form: you can place the filter directly inside the aggregate instead of flattening everything first.

Common Pitfalls

One common mistake is using a normal inner join when the requirement says every left-side row must appear. An inner join will silently drop rows with zero matches.

Another mistake is flattening with DefaultIfEmpty() and then calling plain Count(). That can count placeholder rows instead of actual matches unless you filter out the null side.

Developers also sometimes add a second group by when the original GroupJoin already gave them the grouping they needed. That extra complexity rarely adds value.

Finally, if the query is meant for a database provider, test the translated SQL for both correctness and performance. A readable LINQ query is still worth checking when it becomes a database execution plan.

Summary

  • For left-side rows with counts of right-side matches, GroupJoin is usually the simplest solution.
  • You often do not need to flatten the join before counting.
  • Use DefaultIfEmpty() only when you truly need a row-shaped left join result.
  • If you flatten, count only non-null matches.
  • Keep provider-backed queries simple so they translate cleanly and perform predictably.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.