我尝试执行我的2个表(SQL Server 2012)的递归连接,如下所示:
表:购买
szProductID nQty szSupplierCode
0001 5 A-101
0001 50 A-102
0001 2 A-103
0001 70 A-104
和
表:销售
szProductID nQty szSalesID
0001 10 S-101
0001 20 S-102
0001 20 S-103
0001 50 S-104
我需要这样的结果:
szProductID nQtySales SupplierCode SalesID
0001 5 A-101 S-101
0001 5 A-102 S-101
0001 20 A-102 S-102
0001 20 A-102 S-103
0001 5 A-102 S-104
0001 2 A-103 S-104
0001 43 A-104 S-104
目标是找出szSupplierCode销售的商品数量。我已经找到了很多做选择的例子,但我不确定CTE是否可以解决我的问题。
如果有人可以通过CTE或光标确认这是可行的,我会很感激。
谢谢!
答案 0 :(得分:1)
您可以使用Recursive CTE
:
;WITH PurchaseRN AS (
-- Add row number field to Purchase table
SELECT szProductID, nQty, szSupplierCode,
ROW_NUMBER() OVER (PARTITION BY szProductID
ORDER BY szSupplierCode) AS rn
FROM Purchase
), SalesRN AS (
-- Add row number field to Sales table
SELECT szProductID, nQty, szSalesID,
ROW_NUMBER() OVER (PARTITION BY szProductID
ORDER BY szSalesID) AS rn
FROM Sales
), ConsumePurchases AS (
-- Consume 1st Sales record using 1st Purchase record
SELECT p.szProductID,
IIF(p.nQty > s.nQty, s.nQty, p.nQty) AS nQtySales,
p.szSupplierCode AS SupplierCode,
s.szSalesID AS SalesID,
-- Propagate un-consumed Purchase/Sales quantities to next recursion level
IIF(p.nQty > s.nQty, p.nQty - s.nQty, 0) AS pResidue,
IIF(p.nQty > s.nQty, 0, s.nQty- p.nQty) AS sResidue,
-- Purchase row number processed by current recursion level
1 AS prn,
-- Sales row number processed by current recursion level
1 AS srn
FROM PurchaseRN AS p
INNER JOIN SalesRN AS s ON p.szProductID = s.szProductID
WHERE p.rn = 1 AND s.rn = 1
UNION ALL
SELECT p.szProductID,
-- Calculate Sales quantity consumed by current recursion level
-- If un-consumed Purchase/Sales quantities exist from previous level
-- then use this instead of nQty field.
IIF(c.pResidue > 0,
IIF(c.pResidue > s.nQty, s.nQty, c.pResidue),
IIF(c.sResidue > 0,
IIF(p.nQty > c.sResidue, c.sResidue, p.nQty),
IIF(p.nQty > s.nQty, s.nQty, p.nQty))) AS nQtySales,
p.szSupplierCode AS SupplierCode,
s.szSalesID AS SalesID,
x.pResidue,
x.sResidue,
x.prn AS prn,
x.srn AS srn
FROM PurchaseRN AS p
INNER JOIN SalesRN AS s ON p.szProductID = s.szProductID
INNER JOIN ConsumePurchases AS c ON c.szProductID = s.szProductID
CROSS APPLY (
SELECT -- if previous Purchare record is not fully consumed (c.pResidue > 0)
-- then stay at the same Purchase record (c.prn), else get next record.
CASE
WHEN c.pResidue > 0 THEN c.prn
ELSE c.prn + 1
END AS prn,
-- if previous Sales record is not fully consumed (c.sResidue > 0)
-- then stay at the same Sales record (c.srn), else get next record.
CASE
WHEN c.sResidue > 0 THEN c.srn
ELSE c.srn + 1
END AS srn,
-- calculate Sales quantity left un-cosumed (sResidue) after current record
-- has been processed
CASE
WHEN c.sResidue > 0 THEN IIF(c.sResidue - p.nQty > 0, c.sResidue - p.nQty, 0)
WHEN c.pResidue > 0 THEN IIF(c.pResidue > s.nQty, 0, s.nQty - c.pResidue)
ELSE IIF(p.nQty > s.nQty, p.nQty - s.nQty, 0)
END AS sResidue,
-- calculate Purchase quantity left un-cosumed (pResidue) after current record
-- has been processed
CASE
WHEN c.pResidue > 0 THEN IIF(c.pResidue - s.nQty > 0, c.pResidue - s.nQty, 0)
WHEN c.sResidue > 0 THEN IIF(p.nQty > c.sResidue, p.nQty - c.sResidue, 0)
ELSE IIF(p.nQty > s.nQty, p.nQty - s.nQty, 0)
END AS pResidue) AS x(prn, srn, sResidue, pResidue)
-- Continue until there are no more Purchase/Sales records to process
WHERE p.rn = x.prn AND s.rn = x.srn
)
SELECT szProductID, nQtySales, SupplierCode, SalesID
FROM ConsumePurchases
答案 1 :(得分:0)
谢谢Giorgos ......
我为此工作超过3天,你在一天内完成了......
但我用不同的方法做了它,并且有超过200行......:)
首先我通过计算所有购买和销售(总计)来完成它,如下所示:
Table: PurchaseTmp
szProductID szSupplierCode nQtyPurchase nQtySold
0001 A-101 5 5
0001 A-102 50 50
0001 A-103 2 2
0001 A-104 70 43
之后我逐行迭代......并使用Fetch Next Statement生成超过200行代码。
再次......谢谢Giorgos ......
这个问题已经回答了。
答案 2 :(得分:0)
使用窗口化聚合函数可以重写游标/递归的概率很高。在您的情况下,可以通过计算购买/销售数量的累计总和,然后加入重叠范围来完成:
with a as
(
SELECT id, szProductID, szSupplierCode, nQty,
--cumulative sum of quantities
SUM(nQty) OVER (PARTITION BY szProductID ORDER BY id ROWS UNBOUNDED PRECEDING) AS cumsum
FROM Purchase
)
, b as
(
SELECT id, szProductID, szSalesID, nQty,
--cumulative sum of quantities
SUM(nQty) OVER (PARTITION BY szProductID ORDER BY id ROWS UNBOUNDED PRECEDING) AS cumsum
FROM Sales
)
SELECT
a.szSupplierCode,b.szSalesID,
-- calculate the assigned quantity
CASE WHEN a.cumsum < b.cumsum THEN a.cumsum ELSE b.cumsum END
-CASE WHEN a.cumsum -a.nQty > b.cumsum - b.nQty THEN a.cumsum - a.nQty ELSE b.cumsum - b.nQty END
FROM a
JOIN b
ON a.szProductID = b.szProductID
AND a.cumsum > b.cumsum - b.nQty -- check for overlapping cumultive sums
AND a.cumsum - a.nQty < b.cumsum
id
是确定排序顺序的任何列,例如日期或szSalesID
/ szSupplierCode
。
SQL Server不支持LEAST
/ GREATEST
,否则数量计算会更容易:
LEAST(a.cumsum, b.cumsum) - GREATEST(a.cumsum -a.nQty, b.cumsum - b.nQty)
我劫持了@GiorgosBetsos fiddle:)