我正在使用SQL Server 2012来构建库存计划/重新订购引擎。
我有一堆过时的交易,称之为信用和借记。我想一次做两件事:
表格如下:
CREATE TABLE TX (TDate DATETIME, Qty INT);
INSERT INTO TX VALUES ('2014-03-01', 20);
INSERT INTO TX VALUES ('2014-03-02',-10);
INSERT INTO TX VALUES ('2014-03-03',-20);
INSERT INTO TX VALUES ('2014-03-04',-10);
INSERT INTO TX VALUES ('2014-03-05', 30);
INSERT INTO TX VALUES ('2014-03-06',-20);
INSERT INTO TX VALUES ('2014-03-07', 10);
INSERT INTO TX VALUES ('2014-03-08',-20);
INSERT INTO TX VALUES ('2014-03-09', -5);
我正在使用SQL 2012 SUM OVER()窗口函数来显示这些的运行总计。
select TDate, Qty, RunningTotal, RecommendedReplenish from (
select
TDate,
Qty,
SUM(Qty) OVER (ORDER BY TDate ROWS UNBOUNDED PRECEDING) as RunningTotal,
-1 * (CASE WHEN Qty < 0 AND SUM(Qty) OVER (ORDER BY TDate ROWS UNBOUNDED PRECEDING) < 0
THEN
CASE WHEN Qty > SUM(Qty) OVER (ORDER BY TDate ROWS UNBOUNDED PRECEDING) THEN Qty ELSE SUM(Qty) OVER (ORDER BY TDate ROWS UNBOUNDED PRECEDING) END
ELSE 0 END) as RecommendedReplenish
/* Wrong, does not account for balance resetting to zero */
from TX
) T order by TDate
如果它低于零,我需要找到一种方法将运行总计(又称RT)重置为零。
我的查询,其中数量和RT均为负数,并且将这些数据作为第一个推荐补充的更大(更少负数)。这是第一次正常工作。
我不知道如何从窗口运行总计中扣除这个...如果可能的话,我想在单个语句中执行此操作。
以下是我正在寻求的输出摘要:
TDate Qty R.Tot Replenish New RT
----------- ---- ----- ----------- ---------
3/1/2014 20 20 20
3/2/2014 -10 10 10
3/3/2014 -20 -10 10 0
3/4/2014 -10 -20 10 0
3/5/2014 30 10 30
3/6/2014 -20 -10 10
3/7/2014 10 0 20
3/8/2014 -20 -20 0
3/9/2014 - 5 -25 5 0
Itzik Ben-Gan,Joe Celko或其他SQL英雄,你在外面吗? :)
提前致谢!
答案 0 :(得分:12)
这可以使用基于集合的解决方案来完成:
1.计算正常运行总量(称之为RT)
2.计算RT的运行最小值(称之为MN)
当MN为负时,-MN是您到目前为止必须补充的总量。当MN为负时,令replenish_rt为-MN。因此,新的运行总计(称为new_rt)是rt + replenish_rt。如果您需要返回所需的当前补货数量,请从当前减去过去的replenish_rt(使用LAG)。
这是完整的解决方案查询:
with c1 as ( select *, sum(qty) over(order by tdate rows unbounded preceding) as rt from tx ), c2 as ( select *, -- when negative, mn is the total qty that had to be -- replenished until now, inclusive min(rt) over(order by tdate rows unbounded preceding) as mn_cur from c1 ) select tdate, qty, rt, replenish_rt - lag(replenish_rt, 1, 0) over(order by tdate) as replenish, rt + replenish_rt as new_rt from c2 cross apply(values(case when mn_cur < 0 then -mn_cur else 0 end)) as a1(replenish_rt);干杯, 伊茨克
答案 1 :(得分:1)
呃,基于你的评论,我唯一能想到的就是使用一个我讨厌做的光标。
SQL Fiddle
declare @Date date
declare @Qty int
declare @RR int
declare @running int = 0
declare @results table
(dt date,
qty int,
rt int,
rr int
)
declare C cursor for
select TDate, Qty,
RecommendedReplenish
from (
select
TDate,
Qty,
-1 * (CASE WHEN Qty < 0 AND SUM(Qty) OVER (ORDER BY TDate ROWS UNBOUNDED PRECEDING) < 0
THEN
CASE WHEN Qty > SUM(Qty) OVER (ORDER BY TDate ROWS UNBOUNDED PRECEDING) THEN Qty ELSE SUM(Qty) OVER (ORDER BY TDate ROWS UNBOUNDED PRECEDING) END
ELSE 0 END) as RecommendedReplenish
/* Wrong, does not account for balance resetting to zero */
from TX
) T order by TDate
open c
fetch next from c into @date,@qty,@rr
WHILE @@FETCH_STATUS = 0
BEGIN
set @running = @running + @qty
if @running <0
begin
set @running = 0
end
insert into @results values (@date,@qty,@running,@rr)
fetch next from c into @date,@qty,@rr
end
close c
deallocate c
select
*
from @results
据我所知,给你想要的结果。它不漂亮,我敢肯定它可以使用一些清理,但它确实有效。
+-------------+------+-----+----+
| DT | QTY | RT | RR |
+-------------+------+-----+----+
| 2014-03-01 | 20 | 20 | 0 |
| 2014-03-02 | -10 | 10 | 0 |
| 2014-03-03 | -20 | 0 | 10 |
| 2014-03-04 | -10 | 0 | 10 |
| 2014-03-05 | 30 | 30 | 0 |
| 2014-03-06 | -20 | 10 | 10 |
| 2014-03-07 | 10 | 20 | 0 |
| 2014-03-08 | -20 | 0 | 20 |
| 2014-03-09 | -5 | 0 | 5 |
+-------------+------+-----+----+
答案 2 :(得分:1)
使用临时表,您可以随时应用补货。不确定它是否比@Andrew中的光标方法快得多;可能取决于RT下降到零以下的频率。我使用一个简单的子查询来计算RT,减少输入,同样的结果虽然我同意它需要额外的一步。
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
更新:添加AND TDate > @below_zero_date
作为(次要)改进;只有在表格中有“相当多”的数据时,它才会产生重大影响。