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

ztmd8pv5  于 2023-10-15  发布在  其他
关注(0)|答案(2)|浏览(105)

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?

r1zk6ea1

r1zk6ea11#

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

9rygscc1

9rygscc12#

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:

AccountPeriodamount
AC100January100
AC100February100
AC100March100
AC100April100
AC100May100
AC100June600
AC100July700
AC100August700
AC100September700
AC100October1000
AC100November1000
AC100December1200

相关问题