Window Functions - Running Total with reset

后端 未结 3 499
梦如初夏
梦如初夏 2020-12-09 22:46

I am using SQL Server 2012 to build an inventory planning / reorder engine.

I have a bunch of dated transactions, call them credits and debits. I want to do two thi

3条回答
  •  夕颜
    夕颜 (楼主)
    2020-12-09 23:28

    Using a temp-table you could apply the Replenishment as you go. Not sure if it would be much faster than the cursor approach from @Andrew; probably depends on how often the RT dips below zero. I used a simple subquery to calculate the RT, less typing, same result although I agree it takes an extra step.

    SQL Fiddle

    CREATE TABLE TX (TDate DATETIME, Qty   INT, Replenish INT NULL, RT INT NULL);
    
    INSERT INTO TX VALUES ('2014-03-01', 20, NULL, NULL);  
    INSERT INTO TX VALUES ('2014-03-02',-10, NULL, NULL); 
    INSERT INTO TX VALUES ('2014-03-03',-20, NULL, NULL); 
    INSERT INTO TX VALUES ('2014-03-04',-10, NULL, NULL); 
    INSERT INTO TX VALUES ('2014-03-05', 30, NULL, NULL); 
    INSERT INTO TX VALUES ('2014-03-06',-20, NULL, NULL);  
    INSERT INTO TX VALUES ('2014-03-07', 10, NULL, NULL);  
    INSERT INTO TX VALUES ('2014-03-08',-20, NULL, NULL); 
    INSERT INTO TX VALUES ('2014-03-09', -5, NULL, NULL);
    GO
    
    -- calculate (real) running-totals
    UPDATE TX 
       SET RT = (SELECT SUM(p.Qty)
                   FROM TX p
                  WHERE p.TDate <= upd.TDate)
      FROM TX upd
    GO
    
    -- create a loop to find if there are negative RT's and fix them untill there are none left
    DECLARE @below_zero_date DATETIME,
            @below_zero_value INT
    
    -- SELECT * FROM TX ORDER BY TDate
    
    SELECT @below_zero_value = NULL
    SELECT TOP 1 @below_zero_date = TDate,
                 @below_zero_value = RT
      FROM TX
     WHERE RT < 0
     ORDER BY TDate
    
    WHILE @below_zero_value IS NOT NULL
        BEGIN
            UPDATE TX
               SET RT = RT - @below_zero_value,
                   Replenish = (CASE TDate WHEN @below_zero_date THEN - @below_zero_value ELSE NULL END)
             WHERE TDate >= @below_zero_date
    
            -- SELECT * FROM TX ORDER BY TDate
    
            SELECT @below_zero_value = NULL
    
            SELECT TOP 1 @below_zero_date = TDate,
                         @below_zero_value = RT
              FROM TX
             WHERE RT < 0
               AND TDate > @below_zero_date
             ORDER BY TDate
        END
    
    SELECT * FROM TX ORDER BY TDate
    

    UPDATE: added AND TDate > @below_zero_date as (minor) improvement; it will only have a significant effect when there is 'quite a bit' of data in the table.

提交回复
热议问题