How to Use the LAST_VALUE() Function in SQL Server

In SQL Server, the LAST_VALUE() function returns the last value in an ordered set of values.

LAST_VALUE() is a window function that enables us to get a value from the last row of a query result set or partition. This can be useful for when we want to do stuff such as compare a value from the current row with a value in the last row or include it in a calculation.

You may need to explicitly set the window frame if you want LAST_VALUE() to return the actual last value from the partition or result set. That’s because the default window frame ends with the current row. This is covered in the example below.

Example

Here a basic example to demonstrate:

SELECT
    VendorId,
    ProductName,
    ProductPrice,
    LAST_VALUE( ProductPrice ) OVER ( 
        ORDER BY ProductPrice 
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
        ) AS LAST_VALUE
FROM Products;

Result:

VendorId  ProductName                      ProductPrice  LAST_VALUE
--------  -------------------------------  ------------  ----------
1004      Bottomless Coffee Mugs (4 Pack)  9.99          245       
1003      Hammock                          10            245       
1001      Long Weight (green)              11.99         245       
1004      Tea Pot                          12.45         245       
1001      Long Weight (blue)               14.75         245       
1001      Left handed screwdriver          25.99         245       
1001      Right handed screwdriver         25.99         245       
1002      Sledge Hammer                    33.49         245       
1003      Straw Dog Box                    55.99         245       
1003      Chainsaw                         245           245       

In this example I specified a window frame that included UNBOUNDED FOLLOWING. Specifically, I used a window frame of ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING. This ensures that the window frame covers the whole partition/query result set.

The reason I did that is because the default window frame when using an ORDER BY clause is RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. The CURRENT ROW part means that LAST_VALUE() would simply pick its value from the current row (which means it would simply duplicate the ProductPrice column, which may have made the example slightly confusing).

Here’s what I mean:

SELECT
    VendorId,
    ProductName,
    ProductPrice,
    LAST_VALUE( ProductPrice ) OVER ( 
        ORDER BY ProductPrice 
        ) AS LAST_VALUE
FROM Products;

Result:

VendorId  ProductName                      ProductPrice  LAST_VALUE
--------  -------------------------------  ------------  ----------
1004      Bottomless Coffee Mugs (4 Pack)  9.99          9.99      
1003      Hammock                          10            10        
1001      Long Weight (green)              11.99         11.99     
1004      Tea Pot                          12.45         12.45     
1001      Long Weight (blue)               14.75         14.75     
1001      Left handed screwdriver          25.99         25.99     
1001      Right handed screwdriver         25.99         25.99     
1002      Sledge Hammer                    33.49         33.49     
1003      Straw Dog Box                    55.99         55.99     
1003      Chainsaw                         245           245      

So when we don’t explicitly specify a window frame, it uses a default frame that ends with the current row.

This only happens when we use an ORDER BY clause in a window function. You may assume that we could simply omit the ORDER BY clause, but in this case we can’t. The LAST_VALUE() function requires an ORDER BY clause in its OVER clause.

By the way, using a ROWS/RANGE clause also requires that the ORDER BY clause is specified.

Partitioning the Result Set

We can use a PARTITION BY clause with window functions to partition the result set by a given column. For example, we can partition by vendor:

SELECT
    v.VendorName,
    p.ProductName,
    p.ProductPrice,
    LAST_VALUE( ProductPrice ) OVER ( 
        PARTITION BY p.VendorId
        ORDER BY p.ProductPrice 
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
        ) AS LAST_VALUE
FROM Products p 
INNER JOIN Vendors v
ON v.VendorId = p.VendorId;

Result:

VendorName     ProductName                      ProductPrice  LAST_VALUE
-------------  -------------------------------  ------------  ----------
Mars Supplies  Long Weight (green)              11.99         25.99     
Mars Supplies  Long Weight (blue)               14.75         25.99     
Mars Supplies  Left handed screwdriver          25.99         25.99     
Mars Supplies  Right handed screwdriver         25.99         25.99     
Randy Roofers  Sledge Hammer                    33.49         33.49     
Pedal Medals   Hammock                          10            245       
Pedal Medals   Straw Dog Box                    55.99         245       
Pedal Medals   Chainsaw                         245           245       
Katty Kittens  Bottomless Coffee Mugs (4 Pack)  9.99          12.45     
Katty Kittens  Tea Pot                          12.45         12.45     

Here we got the last value in each partition. Our LAST_VALUE column resets with each new partition.

In this case I used a SQL join to join the Products and Vendors table so that I could present the vendor names instead of just their IDs.

The OVER Clause is Required

We must provide the OVER clause when using window functions such as LAST_VALUE(). Here’s what happens if we remove it:

SELECT
    VendorId,
    ProductName,
    ProductPrice,
    LAST_VALUE( ProductPrice )
FROM Products;

Result:

Msg 10753, Level 15, State 1, Line 5
The function 'LAST_VALUE' must have an OVER clause.

The ORDER BY Clause is Required

And our OVER clause must include an ORDER BY clause. Here’s what happens when we use an OVER clause without an ORDER BY clause:

SELECT
    VendorId,
    ProductName,
    ProductPrice,
    LAST_VALUE( ProductPrice ) OVER( )
FROM Products;

Result:

Msg 4112, Level 15, State 1, Line 5
The function 'LAST_VALUE' must have an OVER clause with ORDER BY.

Documentation

For more information and examples, see Microsoft’s documentation for the LAST_VALUE() function.