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:
How do I ensure that amount 0 remains filled with the previous number?
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
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 |
Please do not upload images of code/data/errors when asking a question.
If you’re on 2022, this is somewhat simpler, due to the implementation of
IGNORE NULLS
. What version are you using?@ThomA I use all year
Are the values always ascending? If so, you just want the maximum.
Are you familiar with what to do when someone answers?
Show 12 more comments