sql >> Base de Datos >  >> RDS >> Sqlserver

Obtenga el valor actualizado de la fila anterior usando LAG sin usar CTE recursivo

El rendimiento aquí sufre de recursivo CTE. CTE por sí solo es azúcar sintáctico.

Solo para estos datos de muestra en particular, esto funciona sin recursividad:

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;

Esto puede ayudar a evaluar el rendimiento, pero fallará si en cualquier bloque total de Debit s superan el total de Credit s. Si BlockTotal es negativo, entonces debe fusionarse con uno o varios bloques siguientes y eso no se puede hacer sin iteración o recursividad.

En la vida real, volcaría CTE3 en la tabla temporal y recorrería los bloques de fusión hasta que no haya más BlockTotal negativos. s.