Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Abrufen des aktualisierten Werts der vorherigen Zeile mit LAG ohne Verwendung von rekursivem CTE

Die Leistung leidet hier unter rekursiv CTE. CTE allein ist nur syntaktischer Zucker.

Nur für diese speziellen Beispieldaten funktioniert dies ohne Rekursion:

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;

Dies kann bei der Bewertung der Leistung helfen, schlägt jedoch fehl, wenn in einem beliebigen Block die Summe Debit ist s überschreiten die Gesamtsumme von Credit s. Wenn BlockTotal negativ ist, muss er mit einem oder mehreren folgenden Blöcken zusammengeführt werden, und das geht nicht ohne Iteration oder Rekursion.

Im wirklichen Leben würde ich CTE3 in eine temporäre Tabelle ausgeben und darüber fahren, indem ich Blöcke zusammenführe, bis es keine negativen BlockTotal mehr gibt s.