简体   繁体   中英

SQL Server 2008 : How to update end date column for previous record

I have some records below. For each client, only one record can have END_DATE=12/31/9998 .

So I need to end date the previous record based on EFFECT_DATE column.

For example, I need to update the END_DATE of ID=2 from 12/31/9998 to 7/17/2017 for CLT_NBR=12375

ID  CLT_NBR IS_PRIMARY  EFFECT_DATE END_DATE
-----------------------------------------------
1   12375     1         8/13/2015   9/30/2015
2   12375     1         10/1/2015   12/31/9998
3   12375     1         7/18/2017   12/31/9998
4   12331     1         2/3/2016    7/8/2016
5   12331     1         7/9/2016    12/31/9998

You can use lead to get the next effective date and subtract one day to update the current row's end date.

with cte as (select t.*,coalesce(dateadd(day,-1,lead(effect_date) over(partition by clt_nbr order by id),'9998-12-31') as new_end_date
             from tbl t)
update cte 
set end_date=new_end_date

For SQL Server 2008, use

with rownums as (select t.*,row_number() over(partition by clt_nbr order by id) as rnum from tbl t)
,cte as (select r1.*,dateadd(day,-1,coalesce(r2.effect_date,'9999-01-01')) as new_end_date
         from rownums r1
         left join rownums r2 on r1.clt_nbr=r2.clt_nbr and r1.rnum=r2.rnum-1
        )
update cte 
set end_date=new_end_date

A solution based on joining and grouping related data. These days I often use CTE's, but before they were popular, one would have had to consider using something like this.

select *
into ##test1
from
(
select ID = 1, CLT_NBR = 12375, IS_PRIMARY = 1, EFFECT_DATE = cast('8/13/2015' as date), END_DATE = cast('9/30/2015' as date) 
union all select ID = 2, CLT_NBR = 12375, IS_PRIMARY = 1, EFFECT_DATE = cast('10/1/2015' as date), END_DATE = cast('12/31/9998' as date) 
union all select ID = 3, CLT_NBR = 12375, IS_PRIMARY = 1, EFFECT_DATE = cast('7/18/2017' as date), END_DATE = cast('12/31/9998' as date) 
union all select ID = 4, CLT_NBR = 12331, IS_PRIMARY = 1, EFFECT_DATE = cast('2/3/2016' as date),  END_DATE = cast('7/8/2016' as date) 
union all select ID = 5, CLT_NBR = 12331, IS_PRIMARY = 1, EFFECT_DATE = cast('7/9/2016' as date),  END_DATE = cast('12/31/9998' as date) 
) x

select * from ##test1

select t.ID, t.CLT_NBR, t.IS_PRIMARY, t.EFFECT_DATE, END_DATE = isnull(dateadd(day,-1,min(t_next.EFFECT_DATE)),'9998-12-31')
from ##test1 t
left join ##test1 t_next on t_next.CLT_NBR = t.CLT_NBR and t_next.effect_date > t.effect_date
group by t.ID, t.CLT_NBR, t.IS_PRIMARY, t.EFFECT_DATE

update t
set END_DATE = helper.END_DATE 
from ##test1 t
left join
(
    select t.ID, t.CLT_NBR, t.IS_PRIMARY, t.EFFECT_DATE, END_DATE = isnull(dateadd(day,-1,min(t_next.EFFECT_DATE)),'9998-12-31')
    from ##test1 t
    left join ##test1 t_next on t_next.CLT_NBR = t.CLT_NBR and t_next.effect_date > t.effect_date
    group by t.ID, t.CLT_NBR, t.IS_PRIMARY, t.EFFECT_DATE
) helper on helper.id = t.id

select * from ##test1

drop table ##test1

you can use query like below for a SQL 2008 compliant answer

CREATE TABLE yourTbl (ID int,CLT_NBR int,IS_PRIMARY int,EFFECT_DATE date, END_DATE date)
INSERT INTO yourTbl VALUES
(1  ,12375,1,'8/13/2015','9/30/2015')
,(2 ,12375,1,'10/1/2015','12/31/9998')
,(3 ,12375,1,'7/18/2017','12/31/9998')
,(4 ,12331,1, '2/3/2016','7/8/2016')
,(5 ,12331,1, '7/9/2016','12/31/9998')




;with y as 
(
SELECT 
    *, 
    ROW_NUMBER() OVER(PARTITION BY CLT_NBR ORDER BY EFFECT_DATE DESC) R
FROM yourTbl WHERE END_DATE ='12/31/9998'
)


UPDATE y1
SET y1.END_DATE= y2.EFFECT_DATE 
FROM 
y y1 JOIN y y2 
ON y1.R=y2.R+1 and y1.CLT_NBR=y2.CLT_NBR


select * from yourTbl

See working demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM