Get previous row updated value using LAG Without using Recursive CTE

∥☆過路亽.° 提交于 2019-12-06 03:22:10

Performance here is suffering from recursive CTE. CTE on it's own is just syntactic sugar.

Just for this particular sample data this works without recursion:

Declare @Tbl as Table(SNO Int,Credit Money,Debit Money,PaidDate Date)
Insert into @Tbl
SELECT * FROM (VALUES (1,0,12,'7Jan16'), (2,10,0,'6Jan16'), (3,15,0,'5Jan16'), (4,0,5,'4Jan16'), (5,0,3,'3Jan16'), (6,0,2,'2Jan16'), (7,20,0,'1Jan16')) AS X(SNO,Credit,Debit,PaidDate);

With CTE1 As (
    Select *
      , CASE WHEN Credit > 0 THEN LEAD(1 - SIGN(Credit), 1, 1) OVER (ORDER BY SNO) ELSE 0 END As LastCrPerBlock
    From @Tbl
), CTE2 As (
    Select *
      , SUM(LastCrPerBlock) OVER (ORDER BY SNO DESC ROWS UNBOUNDED PRECEDING) As BlockNumber
    From CTE1
), CTE3 As (
    Select *
      , SUM(Credit - Debit) OVER (PARTITION BY BlockNumber) As BlockTotal
      , SUM(Credit - Debit) OVER (PARTITION BY BlockNumber ORDER BY SNO ROWS UNBOUNDED PRECEDING) As BlockRunningTotal
    From CTE2
)
Select SNO, Credit, Debit
  , CASE WHEN BlockRunningTotal < 0 THEN -BlockRunningTotal ELSE 0 END As TotalDebit
  , CASE WHEN BlockRunningTotal > 0 THEN CASE WHEN Credit < BlockRunningTotal THEN Credit ELSE BlockRunningTotal END ELSE 0 END As Amount
  , PaidDate
From CTE3
Order By SNO;

This can help evaluate performance, but it will fail if in any block total of Debits exceed total of Credits. If BlockTotal is negative then it must be merged with one or several following blocks and that can't be done without iteration or recursion.

In real life I would dump CTE3 into temporary table and cycle over it merging blocks until there are no more negative BlockTotals.

Palanikumar

From Y.B's answer, added recursive CTE to handle if any BlockTotal have negative. Cannot use while loop for recursion because i converted this query to inline table valued function.(Multi-statement table valued function is very slow)

Declare @Tbl as Table(ReceiptNo varchar(50),Credit Money,Debit Money,PaidDate Date)
Insert into @Tbl
SELECT * FROM (VALUES ('R1',20,0,'1Jan16'),('R2',0,2,'2Jan16'),('R3',0,3,'3Jan16'),('R4',0,5,'4Jan16'),('R5',10,0,'5Jan16'),('R6',0,1,'6Jan16'),('R7',0,10,'7Jan16')) AS X(ReceiptNo,Credit,Debit,PaidDate);

With Receipts As (
    Select 
        SNO = ROW_NUMBER() OVER(ORDER BY PaidDate Desc),ReceiptNo,Credit,Debit,PaidDate,
        LastCrPerBlock = CASE WHEN Credit > 0 THEN LEAD(1 - SIGN(Credit), 1, 1) OVER (ORDER BY PaidDate DESC) ELSE 0 END
    From @Tbl
), Blocks As (
    Select *
      , SUM(LastCrPerBlock) OVER (ORDER BY SNO DESC ROWS UNBOUNDED PRECEDING) As BlockNumber
    From Receipts
), BlockTotal As (
    Select *
      , SUM(Credit - Debit) OVER (PARTITION BY BlockNumber) As BlockTotal
      , SUM(Credit - Debit) OVER (PARTITION BY BlockNumber ORDER BY SNO ROWS UNBOUNDED PRECEDING) As BlockRunningTotal
    From Blocks
),
ReceiptAmount As (
    Select ReceiptNo,
        Amount = CASE WHEN BlockRunningTotal > 0 THEN CASE WHEN Credit < BlockRunningTotal THEN Credit ELSE BlockRunningTotal END ELSE 0 END,
        Debit = IIF(BlockNumber<>LEAD(BlockNumber) OVER(ORDER BY SNO) and BlockRunningTotal<0,ABS(BlockRunningTotal),0),
        PaidDate
    From BlockTotal
),
FinalReceipt2012 As (
    Select 
        SNO = ROW_NUMBER() OVER(ORDER BY PaidDate Desc),ReceiptNo,Amount,Debit,PaidDate,
        Recur = IIF(Exists(Select Top 1 R1.Amount From ReceiptAmount R1 Where Debit>0),1,0)
    From ReceiptAmount
    Where Amount>0 or Debit>0
),
FinalReceipt As (
    Select * From FinalReceipt2012 Where Recur=0 OR SNO=1
    Union All
    Select
        R.SNO,R.ReceiptNo,
        Amount = Case When R.Amount < RP.Debit Then 0 Else R.Amount-RP.Debit End,
        Debit = Case When R.Amount < RP.Debit Then R.Debit + (RP.Debit-R.Amount) Else R.Debit End,
        R.PaidDate,0 As Recur
    From FinalReceipt2012 R 
    Inner Join FinalReceipt RP ON R.SNO=RP.SNO+1
    Where R.Recur=1
)

Select ReceiptNo,Amount,PaidDate From FinalReceipt Where Amount>0

Input:

Output:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!