sql >> データベース >  >> RDS >> Sqlserver

再帰 CTE を使用せずに LAG を使用して前の行の更新された値を取得する

    ここでのパフォーマンスは再帰に苦しんでいます CTE。 CTE 自体は単なるシンタックス シュガーです。

    この特定のサンプル データについては、再帰なしで動作します:

    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;
    

    これはパフォーマンスを評価するのに役立ちますが、ブロックの合計が Debit の場合は失敗します Credit の合計を超えています 秒。 BlockTotal の場合 が負の場合、1 つまたは複数の後続ブロックとマージする必要があり、反復または再帰なしでは実行できません。

    実際には、CTE3 を一時テーブルにダンプし、負の BlockTotal がなくなるまで、ブロックをマージして繰り返します。



    1. INSERT ... ON CONFLICT(id)DO UPDATE ...構文をシーケンスIDで使用するにはどうすればよいですか?

    2. mysqlデータベースを別のコンピューターにエクスポートする方法は?

    3. Hibernate 4とPostgres:テーブルごとにシーケンスを作成する方法は?

    4. 説明なしにストアド プロシージャが一見ハングする