SQL Server 2012 - 根据另一列的值重置 运行 总计

SQL Server 2012 - Reset Running Total Based on the Value of Another Column

我正在使用 SQL Server 2012 尝试在每周数量值达到或超过最小手数时设置新的预测目标。我知道这将需要一个 运行 总计以及一种标记 [​​=31=] 总计需要重置在哪一行的方法。

目前正在尝试递归解决方案,但我也可以使用窗口方法。谢谢!

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 服务器以不同于其他实现(如 PostgreSql)的方式处理 window 函数时,我想不出在 CTE 中执行此操作的方法。如果违反了标准,它甚至可能被认为有问题。

我在下面给出的策略使用带有 while 循环的递归。但这不是 RBAR,所以我认为与您可能担心的这种循环相比,性能不会太糟糕。事实上,如果 windowed 函数在 SQL Server 的递归部分内工作,我将其构造为类似于递归 CTE 的语法。顺便说一下,我实际上在 postgreSQL 中成功地将它实现为递归 CTE。

循环要记住的一件事是我让你的 'flag' 更复杂一点。我将我的标志命名为 'processed',它可以取三个值:1、-1 和 0。0 表示 'not processed',1 表示 'processed',-1 也被处理,但是几乎可以用作 'flag'.

的特殊标记
-- 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 服务器中的可运行结果:sql server
  • 这是我上面讨论的真正的递归 CTE:postgre