sql server为项目分配付款

时间:2016-07-06 09:45:27

标签: sql sql-server tsql

我一直在摸着这个小时一小时仍然无法找到一种方法将$ 30的付款金额分配给下表中的行。

鉴于我有以下项目。负数意味着客户欠债并欠我们这笔金额。现在,客户支付30美元。我们需要将其分配给该项目。

ItemId                               amount sDATE
BD98E890-C7F8-47F4-9125-A68A88DD178D    -10 2016-01-04 00:00:00.000
7E047DE6-0DB7-4EDB-A751-C43BBD4610E5    -20 2016-01-05 00:00:00.000
5004AE1F-2A15-47E5-96FF-69A6C7D35521    -10 2016-01-06 00:00:00.000

支付30美元的输出应该是这样的。

itemId                                   BeforeAllocation   AfterAllocation LeftToAllocate  sDate
BD98E890-C7F8-47F4-9125-A68A88DD178D    -10                 0               30              2016-01-04 00:00:00.000
7E047DE6-0DB7-4EDB-A751-C43BBD4610E5    -20                 0               20              2016-01-05 00:00:00.000
5004AE1F-2A15-47E5-96FF-69A6C7D35521    -10                 -10             0               2016-01-06 00:00:00.000

如果客户支付25美元的部分金额,则输出应为。

    itemId                                   BeforeAllocation   AfterAllocation LeftToAllocate  sDate
    BD98E890-C7F8-47F4-9125-A68A88DD178D    -10                 0               25              2016-01-04 00:00:00.000
    7E047DE6-0DB7-4EDB-A751-C43BBD4610E5    -20                 -5              15              2016-01-05 00:00:00.000
    5004AE1F-2A15-47E5-96FF-69A6C7D35521    -10                 -10             0               2016-01-06 00:00:00.000

代码:

Create table #temp(ItemId UNIQUEIDENTIFIER , amount INT, sDATE DATETIME)

INSERT INTO #temp
        ( ItemId, 
          amount, 
          sDATE )
VALUES (  NEWID(),-10,'2016-01-04' ),
       (  NEWID(),-20,'2016-01-05' ), 
       (  NEWID(),-10,'2016-01-06' ) 


SELECT * FROM (
SELECT 'BD98E890-C7F8-47F4-9125-A68A88DD178D' itemId, -10 BeforeAllocation, 0 AfterAllocation, 30 LeftToAllocate, '2016-01-04 00:00:00.000' sDate     
UNION
SELECT '7E047DE6-0DB7-4EDB-A751-C43BBD4610E5' itemId, -20 BeforeAllocation, 0 AfterAllocation, 20 LeftToAllocate, '2016-01-05 00:00:00.000' sDate
UNION
SELECT '5004AE1F-2A15-47E5-96FF-69A6C7D35521' itemId, -10 BeforeAllocation, -10 AfterAllocation,0 LeftToAllocate, '2016-01-06 00:00:00.000' sDate
)s
ORDER BY sdate

SELECT * FROM (
SELECT 'BD98E890-C7F8-47F4-9125-A68A88DD178D' itemId, -10 BeforeAllocation, 0 AfterAllocation, 25 LeftToAllocate, '2016-01-04 00:00:00.000' sDate     
UNION
SELECT '7E047DE6-0DB7-4EDB-A751-C43BBD4610E5' itemId, -20 BeforeAllocation, -5 AfterAllocation, 15 LeftToAllocate, '2016-01-05 00:00:00.000' sDate
UNION
SELECT '5004AE1F-2A15-47E5-96FF-69A6C7D35521' itemId, -10 BeforeAllocation, -10 AfterAllocation,0 LeftToAllocate, '2016-01-06 00:00:00.000' sDate
)s
ORDER BY sdate

2 个答案:

答案 0 :(得分:0)

  1. 计算表达式BeforeAllocationRT,记录BeforeAllocation的 r unning t 总计(即此行和所有前面行的BeforeAllocation的总和)。如果您正在使用SQL Server 2012或更高版本,则可以使用窗口函数,否则您需要一个笨拙的子表达式 - 有关确切说明,请参阅this question

  2. 计算要分配的金额的表达式

    SELECT Allocation = CASE
        WHEN BeforeAllocationRT + @Payment > 0
            -- pay full amount for this item
            THEN -@BeforeAllocation
        WHEN BeforeAllocationRT + @Payment > @BeforeAllocation
            -- pay partial amount for this item
            THEN -(@BeforeAllocationRT+@Payment)
        ELSE
            -- pay nothing for this item
            0
        END
     , ...
    
  3. 计算AfterAllocationLeftToAllocate的表达式。

    SELECT
        AfterAllocation = BeforeAllocation + Allocation
        ,LeftToAllocate = CASE WHEN BeforeAllocationRT+@Payment>0 THEN @Payment-BeforeAllocationRT ELSE 0 END
        ,...
    
  4. 使用CTE或子表达式合并步骤1,2,3。

  5. 免责声明:我现在无法访问SQL Server实例,因此未对此进行测试。

答案 1 :(得分:0)

试试这个,

DECLARE @PaidAmout INT = 30

;WITH CTE AS
(
    SELECT ItemId, amount, sDATE, ROW_NUMBER() OVER(ORDER BY sDATE) AS RowNo
    FROM #temp 
),
Amount AS
(
    SELECT ItemId, 
            RowNo, 
            amount AS BeforeAllocation, 
            0 AS AfterAllocation, 
            @PaidAmout AS LeftToAllocate, 
            sDATE, 
            @PaidAmout + Amount AS LeftAmount 
    FROM CTE 
    WHERE RowNo = 1

    UNION ALL 
    SELECT c.ItemId, 
            c.RowNo, 
            c.amount AS BeforeAllocation, 
            CASE WHEN a.LeftAmount + c.Amount < 0 THEN a.LeftAmount + c.Amount ELSE 0 END AS AfterAllocation, 
            CASE WHEN a.LeftAmount < 0 THEN 0 ELSE a.LeftAmount END AS LeftToAllocate, 
            c.sDATE, 
            a.LeftAmount + c.Amount AS LeftAmount 
    FROM CTE c
    INNER JOIN Amount a ON a.RowNo + 1 = c.RowNo 
) 

SELECT ItemId, 
        BeforeAllocation, 
        AfterAllocation, 
        LeftToAllocate, 
        sDATE
FROM Amount