這裏有一個遞歸CTE的方法給你:
WITH BucketsRanked AS (
SELECT *, rnk = ROW_NUMBER() OVER (ORDER BY BucketId) FROM Buckets
)
, PaymentsRanked AS (
SELECT *, rnk = ROW_NUMBER() OVER (ORDER BY PaymentId) FROM Payments
)
, PaymentsDistributed AS (
SELECT
b.BucketId,
p.PaymentId,
Bucket = b.MaxAmount,
Payment = p.Value,
BucketRnk = b.rnk,
PaymentRnk = p.rnk,
BucketPayment = CASE
WHEN p.Value > b.MaxAmount
THEN b.MaxAmount
ELSE p.Value
END,
CarryOver = p.Value - b.MaxAmount
FROM
BucketsRanked b,
PaymentsRanked p
WHERE b.rnk = 1 AND p.rnk = 1
UNION ALL
SELECT
b.BucketId,
p.PaymentId,
Bucket = b.MaxAmount,
Payment = p.Value,
BucketRnk = b.rnk,
PaymentRnk = p.rnk,
BucketPayment = CASE
WHEN x.PaymentValue > x.BucketValue
THEN x.BucketValue
ELSE x.PaymentValue
END,
CarryOver = x.PaymentValue - x.BucketValue
FROM PaymentsDistributed d
INNER JOIN BucketsRanked b
ON b.rnk = d.BucketRnk + CASE SIGN(CarryOver) WHEN -1 THEN 0 ELSE 1 END
INNER JOIN PaymentsRanked p
ON p.rnk = d.PaymentRnk + CASE SIGN(CarryOver) WHEN +1 THEN 0 ELSE 1 END
CROSS APPLY (
SELECT
CONVERT(
decimal(18,6),
CASE SIGN(CarryOver) WHEN -1 THEN -d.CarryOver ELSE b.MaxAmount END
),
CONVERT(
decimal(18,6),
CASE SIGN(CarryOver) WHEN +1 THEN +d.CarryOver ELSE p.Value END
)
) x (BucketValue, PaymentValue)
)
SELECT
BucketId,
PaymentId,
Bucket,
Payment,
BucketPayment
FROM PaymentsDistributed
;
基本上,這個查詢需要的首付款和第一桶,計算出這是少,產生第一BucketPayment
項目。記住支付值和存儲桶容量之間的差異,以便在下一次迭代中使用。
在下一次迭代中,根據其符號,差異將用作存儲量或付款。此外,根據差異的符號,查詢將從Payments
表中獲取下一筆付款,或者從Buckets
獲取下一筆存款。(但是,如果差值爲0,則查詢實際上會檢索下一個支付和下一個存儲桶。)然後,將與第一次迭代相同的邏輯應用於新的存儲桶數量和支付值。
迭代持續進行,直到沒有更多桶或沒有更多付款。或直至達到100默認MAXRECURSION值,在這種情況下,你可能想
OPTION (MAXRECURSION n)
追加到上面的查詢,其中n
必須是一個非負整數高達32767規定的最大數量迭代(遞歸)。 (注意:0
實際上代表無限)
你可以試試這個查詢at SQL Fiddle。
你的桶表中是否有任何表示順序的東西?否則你的結果幾乎沒有定義。另外,你的數據類型是什麼,你需要擔心隨機舍入問題嗎? – 2012-07-23 22:59:55
@ X-Zero - 桶表中沒有訂單。只要數量永遠不會超過每個桶的最大數量,並且總價值被分配給一個或多個桶,那麼對桶的任何付款分配都可以。舍入問題絕對重要。數據類型爲十進制(18,6),但是對第三位十進制數字進行舍入。 – lsoliveira 2012-07-23 23:07:24
您可能需要閱讀SQL CLR,並查看它是否比純Transact-SQL解決方案更易於訪問。 – 2012-07-23 23:37:53