sql-server - CTE - 递归更新数量直到消耗总量

标签 sql-server sql-server-2005

我一直在研究 CTE,试图确定是否可以使用订单数量递归更新库存数量记录,直到订单数量被消耗。

以下是表格和记录:

CREATE TABLE [dbo].[myOrder](
  [Account] [float] NOT NULL,
  [Item] [float] NOT NULL,
  [Quantity] [float] NOT NULL
) ON [PRIMARY]

insert into dbo.myOrder values (12345, 1, 50)

CREATE TABLE [dbo].[myInventory](
  [ID] [int] IDENTITY(1,1) NOT NULL,
  [Account] [float] NOT NULL,
  [InvDate] [numeric](18, 0) NOT NULL,
  [Item] [float] NOT NULL,
  [Quantity] [float] NOT NULL,
  [QuantitySold] [float] NOT NULL
) ON [PRIMARY]

insert into dbo.myInventory values (12345, 111287, 1, 45, 40)
insert into dbo.myInventory values (12345, 111290, 1, 40, 0)
insert into dbo.myInventory values (12345, 111290, 1, 12, 0)
insert into dbo.myInventory values (12345, 111291, 1, 25, 0)

myOrder 表中的记录表明要为帐户 12345 创建项目 #1、数量 50 的订单:

Account Item Quantity 
------- ---- --------
12345   1    50

库存表显示帐户 12345 手头有大量商品 #1:

ID Account InvDate Item Quantity QuantitySold
-- ------- ------- ---- -------- ------------
1  12345   111287  1    45       40
2  12345   111290  1    40       0
3  12345   111290  1    12       0
4  12345   111291  1    25       0

目标是开始将 50 个订单数量插入库存记录中,直到 50 个全部消耗完。库存记录按 InvDate 列中的值排序。记录 1 有 5 个剩余数量 (45 - 40 = 5),这将使我们还有 45 个可供订单消耗。记录 2 可以消耗 40 个。记录 3 可以消耗最后 5 个。查询完成后,库存记录将如下所示:

ID Account InvDate Item Quantity QuantitySold
-- ------- ------- ---- -------- ------------
1  12345   111287  1    45       45
2  12345   111290  1    40       40
3  12345   111290  1    12       5
4  12345   111291  1    25       0

注意:库存表存储 QuantitySold,而不是 QuantityRemaining,因此您必须进行数学计算(数量 - QuantitySold)来确定每个库存记录的剩余数量。

我对 CTE 几乎毫无进展。我发现了很多进行选择的示例,其中 CTE 有 2 个部分 - 初始化部分和递归部分联合在一起。我可以用光标来写这个,但我认为可以用 CTE 来完成,我想了解如何操作。

如果有人可以确认 CTE 可以做到这一点或解释如何设置 CTE,我将不胜感激。谢谢!

最佳答案

--@inserted table mimics inserted virtual table from AFTER INSERT triggers on [dbo].[myOrder] table
DECLARE @inserted TABLE 
(
  [Account] [float] NOT NULL,
  [Item] [float] NOT NULL,
  [Quantity] [float] NOT NULL
);

INSERT  @inserted 
VALUES  (12345, 1, 50);

WITH CteRowNumber
AS
(
    SELECT   inv.ID
            ,inv.Account
            ,inv.Item
            ,inv.Quantity
            ,inv.QuantitySold
            ,i.Quantity QuantityOrdered
            ,ROW_NUMBER() OVER(PARTITION BY inv.Account,inv.Item ORDER BY inv.ID ASC) RowNumber
    FROM    myInventory inv
    INNER JOIN @inserted i ON inv.Account = i.Account 
    AND     inv.Item = i.Item 
    WHERE   inv.Quantity > inv.QuantitySold
),  CteRecursive
AS
(
    SELECT   a.ID
            ,a.Account
            ,a.Item
            ,a.RowNumber 
            ,CASE 
                WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold 
                ELSE a.QuantityOrdered
            END QuantitySoldNew
            ,CASE 
                WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold 
                ELSE a.QuantityOrdered
            END RunningTotal
    FROM    CteRowNumber a
    WHERE   a.RowNumber = 1
    UNION ALL
    SELECT   crt.ID
            ,crt.Account
            ,crt.Item
            ,crt.RowNumber
            ,CASE 
                WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN crt.Quantity - crt.QuantitySold
                ELSE crt.QuantityOrdered - prev.RunningTotal
            END QuantitySoldNew
            ,CASE 
                WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold)
                ELSE crt.QuantityOrdered
            END RunningTotal
    FROM    CteRecursive prev
    INNER JOIN CteRowNumber crt ON prev.Account = crt.Account 
    AND     prev.Item = crt.Item 
    AND     prev.RowNumber + 1 = crt.RowNumber
    WHERE   prev.RunningTotal  < crt.QuantityOrdered
)
SELECT   cte.ID
        ,cte.Account
        ,cte.Item
        ,cte.QuantitySoldNew
FROM    CteRecursive cte;
--or CteRecursive can be used to update QuantitySold column from [dbo].[myInventory] table
--UPDATE    myInventory 
--SET       QuantitySold = inv.QuantitySold + cte.QuantitySoldNew
--FROM  myInventory inv
--INNER JOIN CteRecursive cte ON inv.ID = cte.ID;

关于sql-server - CTE - 递归更新数量直到消耗总量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7843129/

相关文章:

sql-server - SQL Server SELECT INTO 和临时表阻塞

sql-server - 如何将 GetDate() 转换为 "YYYY-MM-DD-HHMM"格式?

sql - 我可以在不使用 ROW_NUM () OVER (ORDER BY xxxxx) 的情况下对 Sql Server 中的查询进行分页吗?

sql-server - 多条件存储过程最佳实践/模式

sql - 在 SQL View 中将地址列拆分为单独的列

sql - 删除SQL Server中除最高记录外的所有记录

sql - 为什么不显式 COLLATE 覆盖数据库排序规则?

sql-server - 安装 sql server 2005 express 后 sqlcmd 不工作

sql-server - Visual Studio 数据库架构比较添加未显示

sql - TSQL - 如何在 BEGIN .. END block 内使用 GO?