具有累积值的SQL Update表

时间:2014-07-29 08:07:01

标签: sql sql-server tsql

我有这张桌子:

Date  |StockCode|DaysMovement|OnHand
29-Jul|SC123    |30          |500
28-Jul|SC123    |15          |NULL
27-Jul|SC123    |0           |NULL
26-Jul|SC123    |4           |NULL
25-Jul|SC123    |-2          |NULL
24-Jul|SC123    |0           |NULL

只有顶行具有OnHand值的原因是因为我可以从另一个表中获取此值,该表存储当前任何股票代码的当前数量。

表格中的其他记录来自另一个记录任何特定日期所有动作的表格。

我想更新上面的表格,以便OnHand列根据前一个记录的库存和移动显示该行日期的QtyOnHand,这在更新结束时如下所示:

Date  |StockCode|DaysMovement|OnHand
29-Jul|SC123    |30          |500
28-Jul|SC123    |15          |470
27-Jul|SC123    |0           |455
26-Jul|SC123    |4           |455
25-Jul|SC123    |-2          |451
24-Jul|SC123    |0           |453

我目前正在用CURSOR实现这一目标。但是,性能确实超过了成千上万的记录。

我是否可以运行一些基于SET的UPDATE语句来实现相同的结果?

5 个答案:

答案 0 :(得分:4)

试试这个( Fiddle demo

DECLARE @Movement INT , @OnHandRunning INT  

;WITH CTE AS
(
    SELECT TOP 100 percent DaysMovement, OnHand 
    FROM Table1
    ORDER BY [StockCode], [Date] DESC
)
UPDATE CTE SET @OnHandRunning = OnHand = COALESCE(@OnHandRunning - @Movement, OnHand),
               @Movement = DaysMovement

更新:对于多个StockCodes,您可以修改上述查询,如下所示( Fiddle demo 2 ):

DECLARE @Movement INT , @OnHandRunning INT, @StockCode VARCHAR(10) = '' 

;WITH CTE AS
(
    SELECT TOP 100 percent DaysMovement, OnHand, StockCode  
    FROM Table1
    ORDER BY [StockCode],[Date] DESC
)
UPDATE CTE SET @OnHandRunning = OnHand = 
       CASE WHEN @StockCode<> StockCode THEN OnHand ELSE @OnHandRunning - @Movement END,
       @Movement = DaysMovement,
       @StockCode = StockCode

答案 1 :(得分:2)

这样可行,但不知道它与光标相比如何执行?

--Data
DECLARE @Table TABLE (
    [Date] DATE,
    StockCode VARCHAR(50),
    DaysMovement INT,
    OnHand INT);
INSERT INTO @Table VALUES ('20140729', 'SC123', 30, 500);
INSERT INTO @Table VALUES ('20140728', 'SC123', 15, NULL);
INSERT INTO @Table VALUES ('20140727', 'SC123', 0, NULL);
INSERT INTO @Table VALUES ('20140726', 'SC123', 4, NULL);
INSERT INTO @Table VALUES ('20140725', 'SC123', -2, NULL);
INSERT INTO @Table VALUES ('20140724', 'SC123', 0, NULL);

--Query
SELECT 
    t1.[Date], 
    t1.StockCode, 
    t1.DaysMovement, 
    CASE WHEN t1.OnHand IS NULL THEN MAX(t2.OnHand) - SUM(t2.DaysMovement) ELSE t1.OnHand END AS OnHand 
FROM 
    @Table t1 
    LEFT JOIN @Table t2 ON t1.[Date] < t2.[Date]
GROUP BY 
    t1.[Date], 
    t1.StockCode, 
    t1.DaysMovement, 
    t1.OnHand
ORDER BY 
    t1.[Date] DESC;

结果是:

Date        StockCode   DaysMovement    OnHand
2014-07-29  SC123       30              500
2014-07-28  SC123       15              470
2014-07-27  SC123       0               455
2014-07-26  SC123       4               455
2014-07-25  SC123       -2              451
2014-07-24  SC123       0               453

答案 2 :(得分:0)

如果您无法使用LAG,可以尝试使用递归CTE:

DECLARE @table TABLE ([Date] VARCHAR(100), StockCode VARCHAR(100), DaysMovement INT, OnHand INT)


INSERT INTO @table SELECT '29-Jul', 'SC123', 30, 500
INSERT INTO @table SELECT '28-Jul', 'SC123', 15, NULL
INSERT INTO @table SELECT '27-Jul', 'SC123', 0, NULL
INSERT INTO @table SELECT '26-Jul', 'SC123', 4, NULL
INSERT INTO @table SELECT '25-Jul', 'SC123', -2, NULL
INSERT INTO @table SELECT '24-Jul', 'SC123', 0, NULL


INSERT INTO @table SELECT '19-Jul', 'SC1234', 30, 500
INSERT INTO @table SELECT '18-Jul', 'SC1234', 15, NULL
INSERT INTO @table SELECT '17-Jul', 'SC1234', 0, NULL
INSERT INTO @table SELECT '16-Jul', 'SC1234', 4, NULL
INSERT INTO @table SELECT '15-Jul', 'SC1234', -2, NULL
INSERT INTO @table SELECT '14-Jul', 'SC1234', 0, NULL


;WITH addRowID As (

    SELECT [Date], StockCode, DaysMovement, OnHand, ROW_NUMBER() OVER (PARTITION BY StockCode ORDER BY StockCode, [Date] DESC) AS ROWID
    FROM @table

), CTE AS (
    SELECT [Date], StockCode, DaysMovement, OnHand, ROWID
    FROM addRowID 
    WHERE OnHand IS NOT NULL AND ROWID = 1

    UNION ALL

    SELECT D.[Date], D.StockCode, D.DaysMovement, C.OnHand - C.DaysMovement AS OnHand, D.ROWID
    FROM CTE AS C
    INNER JOIN addRowID AS D
        ON D.StockCode = C.StockCode
        AND D.ROWID = C.ROWID + 1
    WHERE D.OnHand IS NULL

)

SELECT *
FROM CTE
ORDER BY [DATE] DESC

您可以放置​​更新,而不是最后SELECT。不确定性能......

答案 3 :(得分:0)

假设您没有使用sqlserver 2012+,这将使这更容易。试试这个。它会更新你的表格:

DECLARE @t TABLE(Date date, StockCode char(5), DaysMovement int, OnHand int)

INSERT @t VALUES
('29-Jul-2014','SC123',30,500),
('28-Jul-2014','SC123',15,NULL),
('27-Jul-2014','SC123',0 ,NULL),
('26-Jul-2014','SC123',4 ,NULL),
('25-Jul-2014','SC123',-2,NULL),
('24-Jul-2014','SC123',0 ,NULL)

;WITH cte as
(
  SELECT
    Date, 
    StockCode,
    DaysMovement,
    OnHand, 
    max(case when Date = '2014-07-29' THEN OnHand END) over() BaseOnhand, 
    Calc.MovementChange
  FROM @t t
  CROSS APPLY
  (SELECT
     coalesce(SUM(DaysMovement), 0) MovementChange 
   FROM @t
   WHERE
     Date between DateAdd(Day, 1, t.Date) and '2014-07-29'
  ) calc
)
UPDATE CTE SET OnHand = BaseOnhand - MovementChange

SELECT * FROM @t

结果:

Date        StockCode DaysMovement OnHand
2014-07-29  SC123     30           500
2014-07-28  SC123     15           470
2014-07-27  SC123     0            455
2014-07-26  SC123     4            455
2014-07-25  SC123     -2           451
2014-07-24  SC123     0            453

答案 4 :(得分:0)

如果您有SQLServer 2008或旧版本,则三角形JOIN是解决此问题的简便方法

SELECT a.[Date], a.StockCode, a.DaysMovement
     , OnHand = MAX(b.OnHand) - SUM(b.DaysMovement) + a.DaysMovement
FROM   Table1 a
       LEFT JOIN Table1 b ON a.StockCode = b.StockCode AND b.[Date] >= a.[Date]
GROUP BY a.[Date], a.StockCode, a.DaysMovement
ORDER BY a.[Date] DESC

SQLFiddle Demo

如果你有SQLServer 2012或更高版本,在窗口函数中使用ORDER的可能性使查询更容易

SELECT [Date], StockCode, DaysMovement
     , OnHand = MAX(OnHand) OVER (PARTITION BY StockCode)
              - SUM(DaysMovement) OVER (PARTITION BY StockCode 
                                        ORDER BY [Date] Desc)
              + DaysMovement
FROM   Table1

SQLFiddle Demo

两个查询都使用相同的逻辑:对于每一行,只获取OnHand的值(使用MAX并删除当前后每天的移动,这样做就会被删除它也是当前的运动,所以它已经加起来了。这个逻辑避免了每StockCode的第一天(最后一天)的空值。

如果您有SQLServer 2005或更高版本(在此我使用的是SQLServer 2012或更高版本),使用UPDATE可以轻松派生WITH

WITH A AS (
  SELECT [Date], StockCode, DaysMovement
       , OnHand = MAX(OnHand) OVER (PARTITION BY StockCode)
                - SUM(DaysMovement) OVER (PARTITION BY StockCode 
                                          ORDER BY [Date] Desc)
                + DaysMovement
  FROM   Table1
)
UPDATE Table1 SET
  OnHand = A.OnHand
FROM Table1
     INNER JOIN A ON Table1.StockCode = a.StockCode AND Table1.[Date] = a.[Date]