How to take the previous number if it is 0 then take the previous month’s number using LAG syntax

enter image description here

Source Data :

Account Period  Amount
AC100   January 100
AC100   February    0
AC100   March   0
AC100   April   0
AC100   May 0
AC100   June    600
AC100   July    700
AC100   August  0
AC100   September   0
AC100   October 1000
AC100   November    0
AC100   December    1200

I use this query

WITH CTE AS (
    SELECT
        Account,
        Period,
        Amount,
        LAG(Amount, 1, 0) OVER (PARTITION BY Account ORDER BY (SELECT NULL)) AS PreviousAmount
    FROM TableA
)
SELECT
    Account,
    Period,
    CASE WHEN Amount = 0 THEN PreviousAmount ELSE Amount END AS Amount
FROM CTE

but the results only take the previous month which has an amount like the following picture:

enter image description here

How do I ensure that amount 0 remains filled with the previous number?

  • 2

    Please do not upload images of code/data/errors when asking a question.

    – 

  • 2

    If you’re on 2022, this is somewhat simpler, due to the implementation of IGNORE NULLS. What version are you using?

    – 




  • 1

    @ThomA I use all year

    – 

  • 1

    Are the values always ascending? If so, you just want the maximum.

    – 

  • 1

    Are you familiar with what to do when someone answers?

    – 

This is a gaps and islands problem, You can solve it using window function sum() to calculate a running total, this creates islands where rows with consecutive months have the same rn value.

The max() window function used to find the maximum amount within each island for each account.

with cte as (
  select *, sum(Amount) over (partition by Account order by MONTH(Period + ' 1 2014') ) as rn
  from TableA
)
select Account, Period, max(Amount) over (partition by Account, rn ) as Amount
from cte

Results :

Account Period    Amount
AC100   January   100
AC100   February  100
AC100   March     100
AC100   April     100
AC100   May       100
AC100   june      600
AC100   july      700
AC100   August    700
AC100   September 700
AC100   October   1000
AC100   November  1000
AC100   December  1200

Demo here

Using your example data:

create table #data
(
    Account varchar(5),
    [Period] varchar(9),
    amount int
)

insert into #data values
('AC100','January',100),
('AC100','February',0),
('AC100','March',0),
('AC100','April',0),
('AC100','May',0),
('AC100','June',600),
('AC100','July',700),
('AC100','August',0),
('AC100','September',0),
('AC100','October',1000),
('AC100','November',0),
('AC100','December',1200)

You can achieve this with an OUTER APPLY to find the last non-zero amount and return that instead when the amount value is zero:

select
    d.Account,
    d.[Period],
    case when d.amount = 0 then lastNum.lastAmount else d.amount end as amount
from #data d
outer apply
(
    select top 1 amount as lastAmount
    from #data l
    where month(l.[Period] + '1 1900') < month(d.[Period] + '1 1900')
    and l.amount <> 0
    order by month(l.[Period] + '1 1900') desc
) lastNum

Results:

Account Period amount
AC100 January 100
AC100 February 100
AC100 March 100
AC100 April 100
AC100 May 100
AC100 June 600
AC100 July 700
AC100 August 700
AC100 September 700
AC100 October 1000
AC100 November 1000
AC100 December 1200

Leave a Comment