将付款应用到账单的基于集合的方法
Set-based approach to applying payments to bills
我正在尝试显示应用于帐单的付款,我想知道是否有一种方法可以在不使用游标和命令式逻辑的情况下执行此操作。我有账单 table 和付款 table。付款并不总是与账单金额一致,有时多,有时少。我正在尝试创建某种连接,显示每笔付款中有多少应用于每张账单。
假设:
- 账单属于一个账户
- 付款按其 ID 的顺序应用于每张账单
给定账单 table:
ID Amount
1 500
2 500
3 500
场景 1:
付款table
ID Amount
1 750
2 750
使用上述账单 table 和方案 1 付款 table,我希望看到此输出:
Bill ID | Payment ID | Amount Applied
1 1 500
2 2 250
3 2 500
3 2 250
场景 2:
Payments table:
1 300
2 300
3 300
4 300
5 300
鉴于上面的账单 table 和场景 2 付款 table,我希望看到输出:
Bill ID | Payment ID | Amount Applied
1 1 300
1 2 200
2 2 100
2 3 300
2 4 100
3 4 200
3 5 300
我可以使用游标来做到这一点,但我想知道是否有人知道如何使用基于集合的方法来做到这一点 SQL。
谢谢!
SQL 2005 需要更多的步骤,因为它缺少 SUM() OVER(ORDER BY...)
和 LAG()
功能,但基本思路是相同的:计算 运行 的总数账单和付款。
每张账单可以处于三种状态之一:未支付、全额支付或部分支付。比较 运行 总数将确定状态。当前和之前状态之间的差异将是当前周期中应用于该账单的付款金额。
SQL 2005
WITH
Bills_RunningTotal AS (
SELECT
b_ID = b1.ID
,b_Amount = b1.Amount
,b_RunningTotal = SUM(b2.Amount)
FROM Bills b1
INNER JOIN Bills b2
ON (b2.ID <= b1.ID)
GROUP BY b1.ID,b1.Amount
)
,Payments_RunningTotal AS (
SELECT
p_ID = p1.ID
,p_Amount = p1.Amount
,p_RunningTotal = SUM(p2.Amount)
FROM Payments p1
INNER JOIN Payments p2
ON (p2.ID <= p1.ID)
GROUP BY p1.ID,p1.Amount
)
,Bills_Payments_RemainderDue AS (
SELECT
b_ID
,p_ID
,b_Previous_Remainder_Due = CASE
WHEN b_RunningTotal + p_Amount < p_RunningTotal THEN 0
WHEN b_RunningTotal + p_Amount > p_RunningTotal + b_Amount THEN b_Amount
ELSE b_RunningTotal + p_Amount - p_RunningTotal
END
,b_Current_Remainder_Due = CASE
WHEN b_RunningTotal < p_RunningTotal THEN 0
WHEN b_RunningTotal > p_RunningTotal + b_Amount THEN b_Amount
ELSE b_RunningTotal - p_RunningTotal
END
FROM Bills_RunningTotal b
CROSS JOIN Payments_RunningTotal p
)
SELECT
[Bill ID] = b_ID
,[Payment ID] = p_ID
,[Amount Applied] = b_Previous_Remainder_Due - b_Current_Remainder_Due
FROM Bills_Payments_RemainderDue
WHERE b_Previous_Remainder_Due - b_Current_Remainder_Due > 0
ORDER BY [Bill ID],[Payment ID]
SQL 2008+
使用有序窗口函数,可以提高查询效率。
WITH
Bills_Payments_RunningTotal AS (
SELECT
b_ID = b.ID
,p_ID = p.ID
,b_Amount = b.Amount
,p_Amount = p.Amount
,b_RunningTotal = SUM(b.Amount) OVER(PARTITION BY p.ID ORDER BY b.ID)
,p_RunningTotal = SUM(p.Amount) OVER(PARTITION BY b.ID ORDER BY p.ID)
FROM Bills b
CROSS JOIN Payments p
)
,Bills_Payments_RemainderDue AS (
SELECT b_ID,p_ID,b_Amount,p_Amount
,b_Current_Remainder_Due = CASE
WHEN b_RunningTotal < p_RunningTotal THEN 0
WHEN b_RunningTotal > p_RunningTotal + b_Amount THEN b_Amount
ELSE b_RunningTotal - p_RunningTotal
END
FROM Bills_Payments_RunningTotal
)
,Bills_Payments_AmountApplied AS (
SELECT
[Bill ID] = b_ID
,[Payment ID] = p_ID
,[Amount Applied] = ISNULL(LAG(b_Current_Remainder_Due) OVER(PARTITION BY b_ID ORDER BY p_ID),b_Amount) - b_Current_Remainder_Due
FROM Bills_Payments_RemainderDue
)
SELECT [Bill ID],[Payment ID],[Amount Applied]
FROM Bills_Payments_AmountApplied
WHERE [Amount Applied] > 0
ORDER BY [Bill ID],[Payment ID]
我正在尝试显示应用于帐单的付款,我想知道是否有一种方法可以在不使用游标和命令式逻辑的情况下执行此操作。我有账单 table 和付款 table。付款并不总是与账单金额一致,有时多,有时少。我正在尝试创建某种连接,显示每笔付款中有多少应用于每张账单。
假设:
- 账单属于一个账户
- 付款按其 ID 的顺序应用于每张账单
给定账单 table:
ID Amount
1 500
2 500
3 500
场景 1:
付款table
ID Amount
1 750
2 750
使用上述账单 table 和方案 1 付款 table,我希望看到此输出:
Bill ID | Payment ID | Amount Applied
1 1 500
2 2 250
3 2 500
3 2 250
场景 2:
Payments table:
1 300
2 300
3 300
4 300
5 300
鉴于上面的账单 table 和场景 2 付款 table,我希望看到输出:
Bill ID | Payment ID | Amount Applied
1 1 300
1 2 200
2 2 100
2 3 300
2 4 100
3 4 200
3 5 300
我可以使用游标来做到这一点,但我想知道是否有人知道如何使用基于集合的方法来做到这一点 SQL。
谢谢!
SQL 2005 需要更多的步骤,因为它缺少 SUM() OVER(ORDER BY...)
和 LAG()
功能,但基本思路是相同的:计算 运行 的总数账单和付款。
每张账单可以处于三种状态之一:未支付、全额支付或部分支付。比较 运行 总数将确定状态。当前和之前状态之间的差异将是当前周期中应用于该账单的付款金额。
SQL 2005
WITH
Bills_RunningTotal AS (
SELECT
b_ID = b1.ID
,b_Amount = b1.Amount
,b_RunningTotal = SUM(b2.Amount)
FROM Bills b1
INNER JOIN Bills b2
ON (b2.ID <= b1.ID)
GROUP BY b1.ID,b1.Amount
)
,Payments_RunningTotal AS (
SELECT
p_ID = p1.ID
,p_Amount = p1.Amount
,p_RunningTotal = SUM(p2.Amount)
FROM Payments p1
INNER JOIN Payments p2
ON (p2.ID <= p1.ID)
GROUP BY p1.ID,p1.Amount
)
,Bills_Payments_RemainderDue AS (
SELECT
b_ID
,p_ID
,b_Previous_Remainder_Due = CASE
WHEN b_RunningTotal + p_Amount < p_RunningTotal THEN 0
WHEN b_RunningTotal + p_Amount > p_RunningTotal + b_Amount THEN b_Amount
ELSE b_RunningTotal + p_Amount - p_RunningTotal
END
,b_Current_Remainder_Due = CASE
WHEN b_RunningTotal < p_RunningTotal THEN 0
WHEN b_RunningTotal > p_RunningTotal + b_Amount THEN b_Amount
ELSE b_RunningTotal - p_RunningTotal
END
FROM Bills_RunningTotal b
CROSS JOIN Payments_RunningTotal p
)
SELECT
[Bill ID] = b_ID
,[Payment ID] = p_ID
,[Amount Applied] = b_Previous_Remainder_Due - b_Current_Remainder_Due
FROM Bills_Payments_RemainderDue
WHERE b_Previous_Remainder_Due - b_Current_Remainder_Due > 0
ORDER BY [Bill ID],[Payment ID]
SQL 2008+
使用有序窗口函数,可以提高查询效率。
WITH
Bills_Payments_RunningTotal AS (
SELECT
b_ID = b.ID
,p_ID = p.ID
,b_Amount = b.Amount
,p_Amount = p.Amount
,b_RunningTotal = SUM(b.Amount) OVER(PARTITION BY p.ID ORDER BY b.ID)
,p_RunningTotal = SUM(p.Amount) OVER(PARTITION BY b.ID ORDER BY p.ID)
FROM Bills b
CROSS JOIN Payments p
)
,Bills_Payments_RemainderDue AS (
SELECT b_ID,p_ID,b_Amount,p_Amount
,b_Current_Remainder_Due = CASE
WHEN b_RunningTotal < p_RunningTotal THEN 0
WHEN b_RunningTotal > p_RunningTotal + b_Amount THEN b_Amount
ELSE b_RunningTotal - p_RunningTotal
END
FROM Bills_Payments_RunningTotal
)
,Bills_Payments_AmountApplied AS (
SELECT
[Bill ID] = b_ID
,[Payment ID] = p_ID
,[Amount Applied] = ISNULL(LAG(b_Current_Remainder_Due) OVER(PARTITION BY b_ID ORDER BY p_ID),b_Amount) - b_Current_Remainder_Due
FROM Bills_Payments_RemainderDue
)
SELECT [Bill ID],[Payment ID],[Amount Applied]
FROM Bills_Payments_AmountApplied
WHERE [Amount Applied] > 0
ORDER BY [Bill ID],[Payment ID]