我有这张桌子:
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语句来实现相同的结果?
答案 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
如果你有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
两个查询都使用相同的逻辑:对于每一行,只获取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]