In this comprehensive article, I’ll walk you through multiple approaches to get the latest record by date in SQL Server, explaining the pros and cons of each method so you can choose the right one for your specific situation.
SQL Server Get The Latest Record By Date
Before diving into the technical solutions, let’s understand why this is such a common requirement:
- You need to display the most recent customer information
- You want to analyze the latest transaction for each account
- You need to report on the current status of orders
- You’re building a system that tracks the most recent activity per user
Retrieving the latest record in SQL is crucial across various industries and applications.
For this tutorial, I’ll use a typical scenario. Let’s say we have a CustomerActivity table that tracks various customer interactions:
Let us execute the query below to create the table.
CREATE TABLE CustomerActivity (
ActivityID INT PRIMARY KEY,
CustomerID INT,
ActivityDate DATETIME,
ActivityType VARCHAR(50),
Details VARCHAR(255)
);After executing the above query, the table was created successfully as shown below.

INSERT INTO CustomerActivity VALUES
(1, 101, '2024-05-10 09:30:00', 'Purchase', 'Bought a laptop'),
(2, 102, '2024-05-11 14:20:00', 'Inquiry', 'Asked about warranties'),
(3, 101, '2024-05-12 11:45:00', 'Return', 'Returned defective mouse'),
(4, 103, '2024-05-12 16:30:00', 'Purchase', 'Bought a printer'),
(5, 102, '2024-05-13 10:15:00', 'Purchase', 'Bought headphones'),
(6, 101, '2024-05-14 13:25:00', 'Inquiry', 'Asked about upgrades');After executing the above query, I got the expected output as shown in the screenshot below.

Now let’s explore different ways to get the latest activity for each customer.
Approach 1: Using ROW_NUMBER() with Partition
One of the most flexible and efficient approaches is using the ROW_NUMBER() window function:
WITH RankedActivities AS (
SELECT
ActivityID,
CustomerID,
ActivityDate,
ActivityType,
Details,
ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY ActivityDate DESC) AS RowNum
FROM CustomerActivity
)
SELECT
ActivityID,
CustomerID,
ActivityDate,
ActivityType,
Details
FROM RankedActivities
WHERE RowNum = 1;
After executing the above query, I got the expected output as shown in the screenshot below.

Why This Method Works
This approach utilizes a Common Table Expression (CTE) to assign a row number to each record within its customer partition, ordered by date in descending order. Then we filter for row number 1, which represents the latest record for each customer.
The beauty of using ROW_NUMBER() Is that it?
- Returns complete rows (not just dates)
- Works with any date/time column
- Can easily be modified to get the top N records per customer
- Performs well on larger datasets
Approach 2: Using GROUP BY with MAX() Function
Another common approach uses aggregate functions:
SELECT ca.*
FROM CustomerActivity ca
INNER JOIN (
SELECT CustomerID, MAX(ActivityDate) AS MaxDate
FROM CustomerActivity
GROUP BY CustomerID
) latest ON ca.CustomerID = latest.CustomerID AND ca.ActivityDate = latest.MaxDate;After executing the above query, I got the expected output as shown in the screenshot below.

In this method, we first find the maximum date for each customer using a subquery, then join back to the original table to get the complete records.
While this approach is intuitive, it has a potential issue: if there are multiple activities for the same customer on the same date, you’ll get multiple rows. This might be what you want, but often it’s not.
Approach 3: Using a Correlated Subquery
For smaller datasets, a correlated subquery can be a readable approach:
SELECT *
FROM CustomerActivity ca1
WHERE ActivityDate = (
SELECT MAX(ActivityDate)
FROM CustomerActivity ca2
WHERE ca2.CustomerID = ca1.CustomerID
);After executing the above query, I got the expected output as shown in the screenshot below.

While this approach is straightforward to understand, it generally doesn’t perform as well on larger tables because the subquery is executed for each row in the outer query.
Approach 4: Using TOP with PARTITION BY
SQL Server allows us to use TOP with PARTITION BY for a clean solution:
SELECT *
FROM (
SELECT
*,
TOP 1 WITH TIES ActivityID
OVER (PARTITION BY CustomerID ORDER BY ActivityDate DESC) as TopID
FROM CustomerActivity
) AS LatestActivities;
This method is concise and efficient for medium-sized datasets. It’s particularly useful when you need to maintain ties (multiple records with the same latest date).
Method 5: Using the GREATEST() Function (SQL Server 2022+)
If you’re running SQL Server 2022 or newer, you can take advantage of the new GREATEST() function for scenarios where you need to compare multiple date columns:
SELECT
CustomerID,
GREATEST(LastPurchaseDate, LastInquiryDate, LastReturnDate) AS MostRecentActivity
FROM CustomerSummary;This is particularly useful when you have multiple date columns and need to find the latest date regardless of which column it comes from.
Performance Considerations
When dealing with large tables, performance becomes critical. Here are some tips I’ve learned from optimizing queries
- Indexing: Ensure you have an appropriate index on the CustomerID and ActivityDate columns:CREATE INDEX IX_CustomerActivity_CustID_Date ON CustomerActivity(CustomerID, ActivityDate DESC);
- Query Plan Analysis: Use the execution plan to identify bottlenecks in your query.
- Method Selection: For very large tables (millions of rows), the ROW_NUMBER() approach typically performs best.
- Data Distribution: Consider data partitioning if you’re dealing with historical data.
Let’s compare the methods with a simple performance table:
| Method | Small Data | Large Data | Handles Ties | Code Complexity |
|---|---|---|---|---|
| ROW_NUMBER() | Good | Excellent | Yes (with modification) | Medium |
| GROUP BY + MAX | Good | Fair | No | Medium |
| TOP WITH TIES | Good | Good | Yes | Low |
| Correlated Subquery | Good | Poor | No | Low |
| GREATEST() | Excellent | Excellent | N/A | Low |
Best Practices
Below are the key recommendations:
- Choose the right method for your data size:
- For small to medium tables: Any method works fine
- For large tables: Prefer ROW_NUMBER() with proper indexing
- Handle the below cases:
- What if multiple records have the same date?
- What if there are no records for some entities?
- Do you need to consider time along with date?
- Maintain readability:
- Use CTEs to make your queries more readable
- Add comments for complex logic
- Consider creating a view for frequently used latest-record queries
- Test thoroughly:
- Verify your query handles all cases
- Check performance on realistic data volumes
Conclusion
By following these techniques for retrieving the latest records by date, you’ll be well-equipped to get the latest record by date in SQL Server.
You may also like the following articles.
- SQL Server Last Login Date for User
- Get Financial Year from Current Date in SQL Server
- How to Convert VARCHAR to Date in SQL Server
- How to Calculate Age from Date of Birth in SQL Server
- SQL Server Check If Date Is Today
After working for more than 15 years in the Software field, especially in Microsoft technologies, I have decided to share my expert knowledge of SQL Server. Check out all the SQL Server and related database tutorials I have shared here. Most of the readers are from countries like the United States of America, the United Kingdom, New Zealand, Australia, Canada, etc. I am also a Microsoft MVP. Check out more here.