How to Use the LEAD() Function in SQL Server

In SQL Server, LEAD() is a window function that allows us to access a value from a later row in the same result set, without the need to perform a self-join.

We specify the row as an offset from the current row. An offset of 1 means it gets the value from the next row, an offset of 2 means two rows forward, and so on.

There’s also a LAG() function which works the same, except that it gets its value from a previous row.

Syntax

The syntax for the LEAD() function goes like this:

LEAD ( scalar_expression [ , offset ] , [ default ] ) [ IGNORE NULLS | RESPECT NULLS ]
    OVER ( [ partition_by_clause ] order_by_clause )

So scalar_expression can be the name of a column in the query, and we have the option of specifying the offset (offset) and a default value (default) to use in instances where offset exceeds the scope of the partition and there’s no value to be returned.

We must also provide an OVER clause, and that clause must have an ORDER BY clause.

Example

Here’s a basic example to demonstrate:

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

Result:

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

Here, I specified that the LEAD() function get its value from the ProductPrice column, and I specified an offset of 1.

We can see that the LEAD column returns its value from the next row. Each column contains the same value that the ProductPrice column contains on the next row.

On the last row we get null. That’s because there’s no subsequent row for which to retrieve a value.

Using the Default Offset

We can omit the second argument in order to use the default offset value of 1:

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

Result:

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

So we got the same result we got in the previous example.

Changing the Offset

Let’s change the offset to 3:

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

Result:

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

Here I specified an offset of 3, and so we got the value from three rows forward. We also got null in the last three rows of the LEAD column as a result.

Change the Default Value

By default, the LEAD() function returns null if the offset is beyond the scope of the partition and there’s no value to be returned. We can see this happening in the last three rows of the previous example.

We can provide a third argument to change the default value:

SELECT
    VendorId,
    ProductName,
    ProductPrice,
    LEAD( ProductPrice, 3, 0 ) OVER ( 
        ORDER BY ProductPrice
        ) AS LEAD
FROM Products;

Result:

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

Here, I specified a default value of 0, which resulted in the last three rows returning 0 instead of null.

Note that the third argument, if provided, must be type-compatible with the expression provided by the first argument.

Therefore, the following example fails:

SELECT
    VendorId,
    ProductName,
    ProductPrice,
    LEAD( ProductPrice, 3, 'No Value' ) OVER ( 
        ORDER BY ProductPrice
        ) AS LEAD
FROM Products;

Result:

Msg 8114, Level 16, State 5, Line 1
Error converting data type varchar to numeric.

This example failed because I passed a string as the third argument, but the first column is actually defined as decimal(8,2).

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,
    LEAD( ProductPrice ) OVER ( 
        PARTITION BY p.VendorId
        ORDER BY p.ProductPrice
        ) AS LEAD
FROM Products p 
INNER JOIN Vendors v
ON v.VendorId = p.VendorId;

Result:

VendorName     ProductName                      ProductPrice  LEAD 
-------------  -------------------------------  ------------  -----
Mars Supplies  Long Weight (green)              11.99         14.75
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         null 
Randy Roofers  Sledge Hammer                    33.49         null 
Pedal Medals   Hammock                          10            55.99
Pedal Medals   Straw Dog Box                    55.99         245  
Pedal Medals   Chainsaw                         245           null 
Katty Kittens  Bottomless Coffee Mugs (4 Pack)  9.99          12.45
Katty Kittens  Tea Pot                          12.45         null 

This time our LEAD column resets with each new partition.

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

The OVER Clause is Required

As mentioned, we must provide the OVER clause when using window functions like LEAD(). Here’s what happens if we remove it:

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

Result:

Msg 10753, Level 15, State 1, Line 5
The function 'LEAD' 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,
    LEAD( ProductPrice ) OVER( )
FROM Products;

Result:

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

Documentation

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