当每周数量值达到或超过最小批量时,我正在使用 SQL Server 2012 尝试设置新的预测目标。我知道这需要一个运行总计,并且还需要一种方法来标记需要重置运行总计的行。
目前正在尝试递归解决方案,但我也可以使用窗口方法。谢谢!
CREATE TABLE dbo.Table_1
(
ITEM_ID nvarchar(20) NOT NULL,
DUE_DATE datetime NOT NULL,
MIN_LOT_SIZE int NOT NULL,
QUANTITY int NOT NULL
)
INSERT INTO dbo.Table_1 (ITEM_ID, DUE_DATE, MIN_LOT_SIZE, QUANTITY)
VALUES
('ITEM_1', '1/4/2021', 460, 102)
,('ITEM_1', '1/11/2021', 460, 101)
,('ITEM_1', '2/8/2021', 460, 100)
,('ITEM_1', '3/8/2021', 460, 101)
,('ITEM_1', '4/5/2021', 460, 141)
,('ITEM_1', '5/10/2021', 460, 142)
,('ITEM_1', '6/7/2021', 460, 142)
,('ITEM_1', '7/5/2021', 460, 142)
,('ITEM_1', '8/2/2021', 460, 142)
,('ITEM_1', '9/6/2021', 460, 142)
,('ITEM_1', '10/4/2021', 460, 142)
,('ITEM_1', '11/1/2021', 460, 142)
,('ITEM_2', '10/4/2021', 5000, 1057)
,('ITEM_2', '11/1/2021', 5000, 1422)
,('ITEM_3', '1/4/2021', 3050, 17)
,('ITEM_3', '1/11/2021', 3050, 761)
,('ITEM_3', '2/1/2021', 3050, 752)
,('ITEM_3', '3/1/2021', 3050, 760)
,('ITEM_3', '4/5/2021', 3050, 1059)
,('ITEM_3', '5/3/2021', 3050, 1066)
,('ITEM_3', '6/7/2021', 3050, 1066)
,('ITEM_3', '7/5/2021', 3050, 1066)
,('ITEM_3', '8/2/2021', 3050, 1061)
,('ITEM_3', '9/6/2021', 3050, 1066)
,('ITEM_3', '10/4/2021', 3050, 1061)
,('ITEM_3', '11/1/2021', 3050, 1066)
下面我当前的代码适用于 ITEM_1 来标记正确的行,但随后的 ITEM_1 运行总计会在第二个标记之后进入 ITEM_2,并且以后所有项目都会出现这种情况。
查询:
WITH cte AS
(
SELECT
rn, item_id, due_date, min_lot_size, quantity, running_total, flag
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY item_id, due_date) AS rn,
item_id,
due_date,
min_lot_size,
quantity,
SUM(quantity) OVER (PARTITION BY item_id ORDER BY due_date ASC, quantity DESC ROWS UNBOUNDED PRECEDING) AS running_total,
CASE
WHEN SUM(quantity) OVER (PARTITION BY item_id ORDER BY due_date ASC, quantity DESC ROWS UNBOUNDED PRECEDING) >= min_lot_size
THEN 1
ELSE 0
END AS flag
FROM
Table_1) A
WHERE
rn = 1
UNION ALL
SELECT
r.rn, r.item_id, r.due_date, r.min_lot_size, r.quantity,
CASE c.flag
WHEN 1 THEN r.quantity
ELSE c.running_total + r.quantity
END,
CASE
WHEN
CASE c.flag
WHEN 1 THEN r.quantity
ELSE c.running_total + r.quantity
END > c.min_lot_size
THEN 1 ELSE 0
END
FROM
cte c
JOIN
(SELECT
ROW_NUMBER() OVER (ORDER BY item_id, due_date) AS rn,
item_id, due_date, min_lot_size, quantity
FROM Table_1) r ON r.rn = c.rn + 1
)
SELECT *
FROM cte
ORDER BY rn
OPTION (maxrecursion 0)
期望的输出:
最佳答案
对于这样的问题,您必须获得有关运行结果的真正元数据(一行的运行结果不取决于先前的值,而是取决于先前的运行结果)。当 SQL Server 以与 PostgreSql 等其他实现不同且不太直观的方式处理窗口函数时,我想不出在 CTE 内执行此操作的方法。如果它违反了标准,它甚至可能被认为是有缺陷的。
我下面给出的策略使用带有 while
循环的递归。但它不是 RBAR,所以我认为与您可能担心的这样的循环相比,性能不会很糟糕。事实上,如果窗口函数在 SQL Server 的递归部分内工作,我将其构造为类似于递归 CTE 的语法。顺便说一句,我实际上在 postgreSQL 中将其实现为递归 CTE,并取得了成功。
对于循环要记住的一件事是我让你的“标志”变得更复杂一些。我将我的标志称为“已处理”,它可以采用三个值:1、-1 和 0。0 表示“未处理”,1 表示“已处理”,-1 也已处理,但它是一个特殊标记作为您的“旗帜”。
-- The Anchor Part
select *,
result = 0,
processed = 0
into #results
from Table_1
-- The Recursive Part
while exists (select 0 from #results where processed = 0)
update r
set r.result = r.runSum,
r.processed =
case
when r.runSum <= r.min_lot_size then 1
-- first entry that is greater than min_lot_size
when r.runSum - quantity < r.min_lot_size then -1
else 0
end
from (
select *,
runSum = sum(quantity) over(
partition by item_id
order by due_date
)
from #results
where processed = 0
) r;
-- Final Output
select ITEM_ID,
DUE_DATE,
MIN_LOT_SIZE,
QUANTITY,
NEW_QUANTITY = iif(processed = -1, result, 0)
from #results;
- 这是 SQL Server 中的可运行结果:sql server .
- 这是我上面讨论的真正的递归 CTE:postgre .
关于SQL Server 2012 - 根据另一列的值重置运行总计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65330584/