Vous pouvez utiliser un 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