基于集合的方法将付款应用于账单

时间:2015-05-20 15:57:42

标签: sql sql-server sql-server-2005

我正在尝试显示应用于账单的付款,我想知道是否有办法在不使用游标和命令逻辑的情况下执行此操作。我有一个Bills表和Payments表。付款并不总是账单金额,有时超过,有时低于。我正在尝试创建某种联接,以显示每笔付款应用于每个帐单的金额。

假设:

  • 帐单属于单个帐户
  • 付款按其ID
  • 的顺序应用于每张帐单

给出一个Bills表:

ID  Amount
1   500
2   500
3   500

情景1:

付款表

ID  Amount
1   750
2   750

使用上面的Bills表和方案1付款表,我希望看到这个输出:

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

鉴于上面的Bills表和Scene 2 Payments表,我希望看到输出:

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执行此操作。

谢谢!

1 个答案:

答案 0 :(得分:0)

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]