SQL Server Lead and Lag Functions

By:   |   Updated: 2012-04-16   |   Comments (13)   |   Related: 1 | 2 | 3 | 4 | > Functions System


Problem

SQL Server 2012 introduces two new analytical functions, LEAD() and LAG(). In this tip we will be exploring these functions and how to use them.

Solution

These functions access data from a subsequent row (lead) and previous row (lag) in the same result set without the use of a self-join.

The syntax for the Lead and Lag functions is:

LAG|LEAD (scalar_expression [,offset] [,default]) 
    OVER ( [ partition_by_clause ] order_by_clause ) 

Let me explain using this example which creates table Test_table in database TestDB and inserts some data.

CREATE DATABASE [TestDB]
 
--Create testable to hold some data
CREATE TABLE [dbo].[Test_table](
 [id] [int] IDENTITY(1,1) NOT NULL,
 [Department] [nchar](10) NOT NULL,
 [Code] [int] NOT NULL,
 CONSTRAINT [PK_Test_table] PRIMARY KEY CLUSTERED 
(
 [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
--Insert some test data
insert into Test_table values('A',111)
insert into Test_table values('B',29)
insert into Test_table values('C',258)
insert into Test_table values('D',333)
insert into Test_table values('E',15)
insert into Test_table values('F',449)
insert into Test_table values('G',419)
insert into Test_table values('H',555)
insert into Test_table values('I',524)
insert into Test_table values('J',698)
insert into Test_table values('K',715)
insert into Test_table values('L',799)
insert into Test_table values('M',139)
insert into Test_table values('N',219)
insert into Test_table values('O',869)

Our table data will look like this:

Create Test_table on the databse TestDB

Now the query for lead value and lag value will be:

SELECT id,department,Code,
LEAD(Code,1) OVER (ORDER BY Code ) LeadValue,
LAG(Code,1) OVER (ORDER BY Code ) LagValue
FROM test_table

Now the query for leadvalue and lagvalue will be

In the above example, for the first row the Lead value is the value of the next row because the offset is set to 1. The Lag value is NULL because there were no previous rows.

Now if we change the Lead offset to 2 and Lag offset to 3 the output will be as follows:

If we change Lead offset to 2 and Lag offset to 3 the output will be

One thing to note is that NULL values appear, because there are not values for the Lag or Lead.  To replace NULL values with zero add 0 in Lead\Lag function as shown below. 

SELECT id,department,Code,
LEAD(Code,2,0) OVER (ORDER BY Code ) LeadValue,
LAG(Code,3,0) OVER (ORDER BY Code ) LagValue
FFROM test_table
replace NULL with ‘0' add 0 in Lead\Lag function.
Next Steps


sql server categories

sql server webinars

subscribe to mssqltips

sql server tutorials

sql server white papers

next tip



About the author
MSSQLTips author Rajendra Gupta Rajendra Gupta is a Consultant DBA with 14+ years of extensive experience in database administration including large critical OLAP, OLTP, Reporting and SharePoint databases.

This author pledges the content of this article is based on professional experience and not AI generated.

View all my tips


Article Last Updated: 2012-04-16

Comments For This Article




Monday, May 4, 2020 - 3:52:23 AM - santosh Back To Top (85557)

Can we update the values by using Lead or Lag functions ? if no What is the alternative solution?, and if yes what is the solution ?


Friday, December 2, 2016 - 4:39:19 PM - Kris Maly Back To Top (44891)

Awesome

I enjoyed reading this post and recommend others.

Host explains clearly and crisply the functions with examples.

Please keep posting such articles.

Host has good ability in presentation.

Nevermind if the functions are barrowed from Oracle (I used in Oracle) but we have now in MSQL Server that is the best stuff.

Thanks for educating the community and appreciate your volunteership.

Knowlwdge is power and spread the light.

Thanks a bunch


Tuesday, May 17, 2016 - 4:59:49 PM - varun Back To Top (41499)

 HI,

 

I have a requirement to create offset values for years, months, quarters and dates 

ex: 2016 = 0, 2015=1, 2014=2...etc and

2017= -1, 2018 = -2 and so on..

I can get the offset values but not quite sure how to map the same to use in the report so that it can be used as nyearsago , nmonthsago, ndaysago etc

Any Thoughts ?

The query used to get offset values are given below.

select top 10 Datediff(day, CAST(replace(convert(char(10),getdate(),102),'.','-')as date), dbo.DimDate.Date) as ndaysago from dbo.DimDate

select top 10 Datediff(MONTH, CAST(replace(convert(char(10),getdate(),102),'.','-')as date), dbo.DimDate.Date) as nmonthsago from dbo.DimDate

select top 10 Datediff(YEAR, CAST(replace(convert(char(10),getdate(),102),'.','-')as date), dbo.DimDate.Date) as nyearssago from dbo.DimDate

 

 

 

Thanks

Varun Devaraj

 

 


Thursday, April 28, 2016 - 2:34:19 PM - Arif Back To Top (41364)

I use the following code to get the Next_date, how will I make the SQL query which based on employee_id. I mean each employee next date and if there is one row for employee then Null

 

select 

  Id_employee,

  attend_date, 

  ID_Points,

  lead(attend_date) over (order by id_employee, attend_date) as next_date

from Attendance

 

 Id_employee attend_date     ID_Points next_date

1        2016-01-28 00:00:00            2 2015-07-31 00:00:00

9       2015-07-31 00:00:00            3 2015-12-02 00:00:00

9       2015-12-02 00:00:00            1 2016-01-12 00:00:00

9       2016-01-12 00:00:00            2 2016-04-04 00:00:00

9       2016-04-04 00:00:00            2 2015-12-02 00:00:00

16       2015-12-02 00:00:00            1 2015-12-14 00:00:00

39       2015-12-14 00:00:00            3 2015-07-19 00:00:00

40       2015-07-19 00:00:00            1 2015-08-15 00:00:00

40       2015-08-15 00:00:00            3 2015-08-19 00:00:00

40       2015-08-19 00:00:00            2 2015-09-13 00:00:00

 Thanks in Advance

 


Thursday, May 28, 2015 - 9:47:27 AM - J Back To Top (37303)

Refer to the string splitter function by Erikur Eriksson at http://www.sqlservercentral.com/articles/SQL+Server+2012/106903/

Saturday, May 05, 2012 - 9:24:32 PM - Tony C Read The Tip

Interesting. I'm trying to think of a practical use for this functionality. Thoughts?

 


Friday, March 21, 2014 - 6:01:57 AM - Muthurajan Back To Top (29826)

Hi,

     I want to update current record value with previous record values. can i use lag function in update statement or is there any other techniques to do it in sql server 2012?

 

Thanks

Muthu


Friday, May 11, 2012 - 9:38:24 AM - Andrew Davis Back To Top (17418)

Sorry, typo in the SQL:

SELECT
z.msgcode
,z.Station
,z.value
,z.effectivefrom effectivefrom
,CASE
WHEN ( nextStation=Station AND nextmsgcode=msgcode)
THEN nextroweffectivedate
ELSE sysdate+9999 END effectiveto
FROM
(select
x.*
,lead(x.effectivefrom,1) over (order by msgcode,Station,effectivefrom) as nextroweffectivedate
,lead(x.station,1) over (order by msgcode,Station,effectivefrom) as nextStation
,lead(x.msgcode,1) over (order by msgcode,station,effectivefrom) as nextmsgcode
from
stationvalue x
) z


Friday, May 11, 2012 - 9:36:48 AM - Andrew Davis Back To Top (17417)

To Tony C - I have used something similar to create an effective date range.

I have values that are changing on an occasional basis (readings from a power station). By using the Lead function in Oracle, I have been able to generate a date range (i.e. a From_Datetime and To_Datetime columns). This proved very useful for retrieving the value for a particular point in time. The alternative (using a self join and MIN statement) was very slow on a 250m row table!

The Oracle SQL is here (should be self explanatory):

SELECT
z.msgcode
,z.Station
,z.value
,z.effectivefrom effectivefrom
,CASE
WHEN ( nextStation=Station AND nextmsgcode=msgcode)
THEN nextroweffectivedate
ELSE sysdate+9999 END effectiveto
FROM
(select
x.*
,lead(x.effectivefrom,1) over (order by msgcode,Station,effectivefrom) as nextroweffectivedate
,lead(x.BMUNITCODE,1) over (order by msgcode,Station,effectivefrom) as nextStation
,lead(x.msgcode,1) over (order by msgcode,bmunitcode,effectivefrom) as nextmsgcode
from 
 stationvalue x
 )  z


Saturday, May 5, 2012 - 9:50:04 PM - Tony C Back To Top (17284)

Seems to be borrowed to some extent from Oracle's analytical functions...LEAD, LAG (, , ) OVER ()


Saturday, May 5, 2012 - 9:24:32 PM - Tony C Back To Top (17283)

Interesting. I'm trying to think of a practical use for this functionality. Thoughts?


Tuesday, April 17, 2012 - 2:09:51 AM - Lakki Back To Top (16959)

 

Thanks for your post and Good to know new Functions


Monday, April 16, 2012 - 9:01:20 AM - jeevan Back To Top (16939)

handy functions....but why did it take so long........?

 


Monday, April 16, 2012 - 8:40:09 AM - Hassan Parthasarathy Back To Top (16936)

Good to know;

Thanks for the post!

Partha















get free sql tips
agree to terms