我希望用MS SQL中的CTE解决下面的问题,但我正在碰壁砖。
这是我的问题。
订单表:
OrderID Item Quantity
------------------------
1 pen 80
2 pen 30
3 pen 25
库存表:
Inv ID Lot Item Quantity
---------------------------
1 001 pen 100
2 002 pen 20
3 003 pen 30
我需要做的是处理订单,以便第一个订单来自第1批,第二个订单来自第1批和第2批,第三个订单来自第2批和第3批。
我需要知道哪个订单来自哪个订单,这意味着我不能只是将订单分组。
所以基本上我需要类似的东西:
OrderID Item QuantityOrdered Lot QuantityFromLot
--------------------------------------------------
1 pen 80 001 80
2 pen 30 001 20
2 pen 30 002 10
3 pen 25 002 10
3 pen 25 003 15
有没有办法用CTE做到这一点?如果没有,你会推荐什么?
答案 0 :(得分:1)
对于这类问题,C#或其他一些应用层解决方案可能是最好的方法。
但是有可能在SQL中做,虽然它有点复杂。
递归CTE的第一部分将采用OrderID = 1和InvID = 1并对其进行计算,从而得到LeftInLot
> 0或LeftToServe
> 0
第二部分现在需要基于第一部分结果的两个不同的逻辑,这是通过子查询完成下一个批次和下一个项目并使用一堆CASE来确定使用哪个 - 以及更少的CASE来提供准确的数据下一次递归继续。
看起来像这样:
;WITH CTE_Orders AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY Item ORDER BY OrderID) AS RN
FROM dbo.Orders
)
, CTE_Inventory AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY Item ORDER BY InvID) AS RN
FROM dbo.Inventory
)
, CTE AS
(
SELECT o.RN AS OrderRN,
inv.RN AS InvRN,
OrderID ,
o.Item ,
o.Quantity AS OrderedQuantity ,
InvID ,
Lot ,
inv.Quantity AS InvQuantity,
CASE WHEN inv.Quantity - o.Quantity > 0 THEN o.Quantity ELSE inv.Quantity END AS ServedQuantity ,
CASE WHEN inv.Quantity - o.Quantity > 0 THEN 0 ELSE o.Quantity - inv.Quantity END AS LeftToServe,
CASE WHEN inv.Quantity - o.Quantity > 0 THEN inv.Quantity - o.Quantity ELSE 0 END AS LeftInLot
FROM CTE_Orders o
INNER JOIN CTE_Inventory inv ON o.Item = inv.Item
--WHERE OrderID = 1 AND InvID = 1
WHERE o.RN =1 AND inv.RN = 1
UNION ALL
SELECT CASE WHEN c1.LeftInLot <=0 THEN c1.OrderRN ELSE c2.OrderRN END AS OrderRN
,CASE WHEN c1.LeftInLot <=0 THEN c2.InvRN ELSE c1.InvRN END AS InvRN
,CASE WHEN c1.LeftInLot <=0 THEN c1.OrderID ELSE c2.OrderID END AS OrderID
,CASE WHEN c1.LeftInLot <=0 THEN c1.Item ELSE c2.Item END AS Item
,CASE WHEN c1.LeftInLot <=0 THEN c1.OrderedQuantity ELSE c2.OrderedQuantity END AS OrderedQuantity
,CASE WHEN c1.LeftInLot <=0 THEN c2.InvID ELSE c1.InvID END AS InvID
,CASE WHEN c1.LeftInLot <=0 THEN c2.Lot ELSE c1.Lot END AS Lot
,CASE WHEN c1.LeftInLot <=0 THEN c2.InvQuantity ELSE c1.LeftInLot END AS InvQuantity
,CASE WHEN CASE WHEN c1.LeftInLot <=0 THEN c2.InvQuantity ELSE c1.LeftInLot END - CASE WHEN c1.LeftInLot <=0 THEN c1.LeftToServe ELSE c2.OrderedQuantity END > 0
THEN CASE WHEN c1.LeftInLot <=0 THEN c1.LeftToServe ELSE c2.OrderedQuantity END
ELSE CASE WHEN c1.LeftInLot <=0 THEN c2.InvQuantity ELSE c1.LeftInLot END
END AS ServedQuantity
,CASE WHEN CASE WHEN c1.LeftInLot <=0 THEN c2.InvQuantity ELSE c1.LeftInLot END - CASE WHEN c1.LeftInLot <=0 THEN c1.LeftToServe ELSE c2.OrderedQuantity END > 0
THEN 0
ELSE CASE WHEN c1.LeftInLot <=0 THEN c1.LeftToServe ELSE c2.OrderedQuantity END - CASE WHEN c1.LeftInLot <=0 THEN c2.InvQuantity ELSE c1.LeftInLot END
END AS LeftToServe
,CASE WHEN CASE WHEN c1.LeftInLot <=0 THEN c2.InvQuantity ELSE c1.LeftInLot END - CASE WHEN c1.LeftInLot <=0 THEN c1.LeftToServe ELSE c2.OrderedQuantity END > 0
THEN CASE WHEN c1.LeftInLot <=0 THEN c2.InvQuantity ELSE c1.LeftInLot END - CASE WHEN c1.LeftInLot <=0 THEN c1.LeftToServe ELSE c2.OrderedQuantity END
ELSE 0
END AS LeftInLot
FROM CTE c1
INNER JOIN
(
SELECT o2.RN AS OrderRN,
inv2.RN AS InvRN,
InvID ,
Lot ,
inv2.Item ,
inv2.Quantity AS InvQuantity,
OrderID ,
o2.Quantity AS OrderedQuantity
FROM
CTE_Inventory inv2
INNER JOIN CTE_Orders o2 ON inv2.Item = o2.Item
) c2
ON c1.Item = c2.Item AND
((c2.InvRN = c1.InvRN + 1 AND c2.OrderRN = c1.OrderRN AND c1.LeftInLot <= 0 ) OR (c2.OrderRN = c1.OrderRN + 1 AND c2.InvRN = c1.InvRN AND c1.LeftInLot>0))
)
SELECT * FROM CTE
ORDER BY item,OrderID
<强> SQLFiddle DEMO - fixed 强>
PS:因为你不能真正依赖OrderID
和InvID
是连续的值而没有任何空白 - 就像我的例子所假设的那样(c2.OrderID = c1.OrderID + 1
),另外一个并发症ROW_NUMBER
应该完成。(已修复)
修改强>
更新了解决方案以处理多个项目。开始时有几个CTE计算为项目分区的ROW_NUMBERS,并在JOIN中使用它们而不是ID。