SQL 服务器 - 运行 带结转的总数

SQL Server - Running Total with Carry Forward

在以下方面需要一些帮助:

Table #Data 包含产品超过 5 天的期初和期末库存

Table #BackData 包含一些 post 日期交易

我如何使用 运行 总计更新 table #Data 包括结转

CREATE TABLE #Data (    
    Prod VARCHAR(20)
    ,SDate DATE
    ,OStock INT
    ,CStock INT
    )

CREATE TABLE #BackData (
    Prod VARCHAR(20)
    ,SDate DATE
    ,CStock INT
    )


INSERT INTO #Data
SELECT 'p1', '2016-06-06', 10, 10
UNION ALL
SELECT 'p1', '2016-06-07', 10, 14
UNION ALL
SELECT 'p1', '2016-06-08', 14, 13
UNION ALL
SELECT 'p1', '2016-06-09', 13, 13
UNION ALL
SELECT 'p1', '2016-06-10', 13, 11

INSERT INTO #BackData
SELECT 'p1', '2016-06-06', 2
UNION ALL
SELECT 'p1', '2016-06-07', 4
UNION ALL
SELECT 'p1', '2016-06-09', -1
UNION ALL
SELECT 'p1', '2016-06-10', -2

DROP TABLE #Data
DROP TABLE #BackData

期望的输出:

Prod|   SDate   |OStock |CStock|
p1  |2016-06-06 |10     |12    |
p1  |2016-06-07 |12     |16    |
p1  |2016-06-08 |16     |16    |
p1  |2016-06-09 |16     |15    |
p1  |2016-06-10 |15     |13    |

编辑

这是我在得到答案之前设法写的,使用了两次更新,因为实际的 table 有太多的列无法在单个查询中使用。

UPDATE D
SET D.CStock = FL.NewCStock
FROM #Data D
INNER JOIN (
    SELECT DT.Prod
        ,DT.SDate
        ,SUM(IIF(RwNm = 1, DT.CStock, 0) + ISNULL(BD.CStock, 0)) OVER (
            PARTITION BY DT.Prod ORDER BY DT.SDate ROWS UNBOUNDED PRECEDING
            ) NewCStock
    FROM (
        SELECT Prod
            ,SDate
            ,CStock
            ,ROW_NUMBER() OVER (
                PARTITION BY Prod ORDER BY SDate
                ) AS RwNm
        FROM #Data
        ) DT
    LEFT JOIN #BackData BD ON DT.Prod = DT.Prod
        AND BD.SDate = DT.SDate
    ) FL ON D.Prod = FL.Prod
    AND D.SDate = FL.SDate

UPDATE D
SET D.OStock = PV.NewOStock
FROM #Data D
INNER JOIN (
    SELECT Prod
        ,SDate
        ,ISNULL(LAG(CStock) OVER (
                PARTITION BY Prod ORDER BY SDate
                ), CStock) AS NewOStock
    FROM #Data
    ) PV ON D.Prod = PV.Prod
    AND D.SDate = PV.SDate

您可以使用以下查询 UPDATE:

;WITH ToUpdate AS (
   SELECT d1.OStock, d1.CStock,
          COALESCE(LAG(d2.CStock2) OVER (PARTITION BY d2.Prod 
                                         ORDER BY d2.SDate),
                   d1.OStock) AS OStock2,
          d2.CStock2  
   FROM #Data AS d1
   JOIN (
      SELECT d.Prod, d.SDate, d.OStock, d.CStock, 
             COALESCE(t.newCStock, 
                      LAG(t.newCStock) OVER (PARTITION BY d.Prod 
                                             ORDER BY d.SDate)) AS CStock2
      FROM #Data AS d
      LEFT JOIN (
         SELECT bd.Prod, bd.SDate, 
                drn.CStock + SUM(bd.CStock) OVER (PARTITION BY bd.Prod 
                                                  ORDER BY bd.SDate) AS newCStock
         FROM #BackData AS bd
         INNER JOIN (
            SELECT Prod, CStock, 
                   ROW_NUMBER() OVER (PARTITION BY Prod ORDER BY SDate) AS rn
            FROM #Data
         ) AS drn ON bd.Prod = drn.Prod AND drn.rn = 1
      ) AS t ON t.Prod = d.Prod AND t.SDate = d.SDate
   ) AS d2 ON d1.Prod = d2.Prod AND d1.SDate= d2.SDate
)
UPDATE ToUpdate
SET OStock = OStock2,
    CStock = CStock2

这看起来非常复杂,但我想不出更简单的事情。

Demo here

您可以在递归 CTE 的帮助下重建 #Data table 中的值:

;WITH cte AS (
SELECT top 1 d.Prod,
        d.SDate,
        d.OStock,
        d.OStock + b.CStock as CStock
FROM #Data d
LEFT JOIN #BackData b
    ON b.Prod = d.Prod and b.SDate = d.SDate
ORDER BY d.SDate ASC
UNION ALL
SELECT  c.Prod,
        DATEADD(day,1,c.SDate),
        c.CStock,
        c.CStock + ISNULL(b.CStock,0)
FROM cte c
INNER JOIN #Data d 
    ON d.Prod = c.Prod AND d.SDate = DATEADD(day,1,c.SDate)
OUTER APPLY (SELECT CStock FROM #BackData b WHERE b.Prod = d.Prod and b.SDate = d.SDate) as b
)

SELECT *
FROM cte

输出:

Prod                 SDate      OStock      CStock
-------------------- ---------- ----------- -----------
p1                   2016-06-06 10          12
p1                   2016-06-07 12          16
p1                   2016-06-08 16          16
p1                   2016-06-09 16          15
p1                   2016-06-10 15          13

待更新#Data

UPDATE d
SET OStock = c.OStock, CStock = c.CStock
FROM #Data d
INNER JOIN cte c
    ON c.Prod = d.Prod AND c.SDate = d.SDate

结果不应该是这样的:

Prod SDate      OStock CStock
p1   2016-06-06     10     12
p1   2016-06-07     12     20 (#Data CStock 14 + #BakData 2 + 4)
p1   2016-06-08     20     19 (#Data CStock 13 + #BakData 2 + 4)
p1   2016-06-09     19     18 (#Data CStock 13 + #BakData 2 + 4 - 1)
p1   2016-06-10     18     14 (#Data CStock 11 + #BakData 2 + 4 - 1 -2)

这个查询会产生上面的结果

update  d
set OStock  = d.OStock + a.OAdj,
    CStock  = d.CStock + a.CAdj
from    #Data d
    cross apply
    (
        select  OAdj  = sum(case when Oflag = 1 then x.CStock else 0 end),
            CAdj =  sum(x.CStock)
        from    
        (
            select  *, Oflag = case when x.SDate = d.SDate then 0 else 1 end
            from    #BackData x
            where   x.Prod      = d.Prod 
            and x.SDate     <= d.SDate
        ) x
    ) a

根据您的预期输出,您似乎正在根据 2016-06-06 的数字重新计算每日期初/期末余额

这是一个可以为您提供预期输出的解决方案。

; with 
cte as
(
    select  Prod, SDate, OStock, CStock, 
            rn = row_number() over (partition by Prod order by SDate)
    from    #Data
),
adj as
(
    select  Prod, SDate, CStock
    from    cte
    where   rn  = 1
    union all
    select  Prod, SDate, CStock
    from    #BackData
)
update  d
set OStock  = coalesce(o.OStock, d.OStock),
    CStock  = c.CStock
from    #Data d
    cross apply
    (
        select  OStock = sum(x.CStock)
        from    adj x
        where   x.Prod  = d.Prod
        and x.SDate < d.SDate
    ) o
    cross apply
    (
        select  CStock = sum(x.CStock)
        from    adj x
        where   x.Prod  = d.Prod
        and x.SDate <= d.SDate
    ) c

结果:

p1  2016-06-06  10  12
p1  2016-06-07  12  16
p1  2016-06-08  16  16
p1  2016-06-09  16  15
p1  2016-06-10  15  13