LINQ
C#
date filtering
record selection
programming tips

How to select only the records with the highest date in LINQ

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Selecting records with the highest date is a common query pattern for latest status, latest event, and deduplicated feeds. The correct LINQ shape depends on whether you need a global maximum or a maximum per group. Handling ties and provider translation behavior is essential for correct and deterministic results.

Example Data Model

The examples below use a simple in-memory model.

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5public class Record
6{
7    public int Id { get; set; }
8    public string Category { get; set; }
9    public DateTime EventDate { get; set; }
10}
11
12var records = new List<Record>
13{
14    new Record { Id = 1, Category = "A", EventDate = new DateTime(2026, 1, 10) },
15    new Record { Id = 2, Category = "A", EventDate = new DateTime(2026, 2, 5) },
16    new Record { Id = 3, Category = "B", EventDate = new DateTime(2026, 1, 12) },
17    new Record { Id = 4, Category = "B", EventDate = new DateTime(2026, 1, 12) },
18};

Global Highest Date Records

If you need all records tied to the maximum date across full collection, use Max then Where.

csharp
1DateTime maxDate = records.Max(r => r.EventDate);
2List<Record> latest = records
3    .Where(r => r.EventDate == maxDate)
4    .ToList();
5
6Console.WriteLine(maxDate);
7Console.WriteLine(latest.Count);

This returns all ties, which is usually safer than returning one arbitrary row.

Highest Date Per Group

For latest record selection per category or key, use GroupBy.

csharp
1var latestPerCategory = records
2    .GroupBy(r => r.Category)
3    .SelectMany(g =>
4    {
5        DateTime groupMax = g.Max(x => x.EventDate);
6        return g.Where(x => x.EventDate == groupMax);
7    })
8    .ToList();
9
10foreach (var r in latestPerCategory)
11{
12    Console.WriteLine($"{r.Category} {r.Id} {r.EventDate:yyyy-MM-dd}");
13}

SelectMany preserves ties within each group.

Deterministic One-Row Selection

If business rules require exactly one row per group, define a tie-breaker.

csharp
1var oneRowPerCategory = records
2    .GroupBy(r => r.Category)
3    .Select(g => g
4        .OrderByDescending(x => x.EventDate)
5        .ThenByDescending(x => x.Id)
6        .First())
7    .ToList();

Without tie-breakers, different providers can return different rows when dates match.

Query Provider Considerations

In IQueryable scenarios, ensure query shape translates efficiently to SQL. A common database-friendly pattern uses grouped max then join-like filtering.

csharp
1var query =
2    from r in db.Records
3    group r by r.Category into g
4    let maxDate = g.Max(x => x.EventDate)
5    from r2 in g
6    where r2.EventDate == maxDate
7    select r2;

Inspect generated SQL and index coverage on grouping and date columns.

Handling Nullable Dates

If date is nullable, filter nulls or decide explicit null semantics before Max.

csharp
var nonNull = records
    .Where(r => r.EventDate != default(DateTime))
    .ToList();

For DateTime?, use null checks explicitly before aggregation.

Date Precision and Business Rules

Sometimes business logic is date-only while data includes time components. Normalize precision before comparison.

csharp
1var latestByDay = records
2    .GroupBy(r => r.EventDate.Date)
3    .OrderByDescending(g => g.Key)
4    .First()
5    .ToList();

Always align comparison precision with domain requirements.

Performance Notes

For large in-memory collections, Max plus one Where pass is efficient for global maximum. For grouped queries, grouping cost can be substantial at scale.

Database-backed workloads should rely on indexed date columns and verify execution plans. Performance assumptions should be validated with realistic dataset sizes.

Common Pitfalls

  • Using First after sorting when ties should be preserved. Fix: use max-plus-filter shape when all tied records are required.
  • Forgetting deterministic tie-breaker for one-row output. Fix: add secondary ordering, such as by identifier.
  • Mixing in-memory and provider expressions carelessly. Fix: keep query provider translation in mind and inspect generated SQL.
  • Comparing full timestamps when business logic is date-only. Fix: normalize precision before filtering.
  • Ignoring nullable date handling. Fix: define explicit null strategy before aggregation.

Summary

  • Use Max plus Where for records tied to global highest date.
  • Use GroupBy plus max filter for latest records per key.
  • Add tie-break rules when exactly one record per group is required.
  • Align date precision and null handling with business rules.
  • Verify query translation and indexing for database-backed LINQ.

Course illustration
Course illustration

All Rights Reserved.