SQL - 使用参考 table 将值拆分到桶中

SQL - split values into buckets using reference table

我试图在下面描述挑战;但也许 最好的 理解方式可能是 运行 样本 SQL 并从样本输出 table (@IncomingSplitBucket)

我确定有一种 eloquent 的编码方式 – 但它超出了我的最大努力。

挑战的标题是:

将数字拆分到桶中

每个客户都有付款桶 (@bucket)。我希望在付款时将付款分配到存储桶中,请参阅 table (@incoming)。

付款可以跨桶,可以是 +/- 金额。

使用 (@incoming) 和 (@buckets) 信息——目的是将付款分配到桶中。当付款跨越一个桶金额时,应该分开付款。

table @IncomingSplitBucket – 提供所需的输出。理解需求的一种方法是查看这个并向后工作。

我已经尝试过很多解决这个问题的方法,但都失败了。

/*
Please run code and review
*/

--===========================================
--t1 - PAYMENT SCHEDULE SPLIT INTO BUCKET
--===========================================
DECLARE @bucket TABLE (
CustID INT,
BucketSeqID char(1),
Amount money
)

INSERT INTO @bucket
SELECT 1,'a', '1000' union
SELECT 1,'b', '1000' union
SELECT 1,'c', '2000' union
SELECT 1,'d', '2000' union

SELECT 2,'a', '5000'union
SELECT 2,'b', '5000'union
SELECT 2,'c', '1000'union
SELECT 2,'d', '1000'union

SELECT 3,'a', '5000' union
SELECT 3,'b', '5000' 
--===========================================
--t2 - PAYMENTS COMING IN
--===========================================
DECLARE @incoming TABLE (
CustID INT,
IncomingSeqID INT,
Amount money
)

INSERT INTO @incoming
SELECT 1,1, '1000' union
SELECT 1,2, '2000' union
SELECT 1,3, '3000' union

SELECT 2,1, '5000' union
SELECT 2,2, '3000' union
SELECT 2,3, '2000' union
SELECT 2,4, '2000' union

SELECT 3,1, '3000' union
SELECT 3,2, '3000' union
SELECT 3,3, '3000' union
SELECT 3,4, '1000'

--=================================================================
--t3 - THIS IS WHAT THE OUTPUT DATA SHOULD LOOK LIKE
--================================================================
DECLARE @IncomingSplitBucket TABLE (
CustID INT,
IncomingSeqID INT,
BucketSeqID char(1),
AmountBucket money
)

INSERT INTO @IncomingSplitBucket

SELECT 1,1,'a','1000' union
SELECT 1,2,'b','1000' union
SELECT 1,2,'c','1000' union
SELECT 1,3,'c','1000' union
SELECT 1,3,'d','2000' union

SELECT 2,1,'a','5000' union
SELECT 2,2,'b','3000' union
SELECT 2,3,'b','2000' union
SELECT 2,4,'c','1000' union
SELECT 2,4,'d','1000' union

SELECT 3,1,'a','3000' union
SELECT 3,2,'a','2000' union
SELECT 3,2,'b','1000' union
SELECT 3,3,'b','3000' union
SELECT 3,4,'b','1000' 

--=================================================================
--Outputs and Data Checks
--================================================================

--REVIEW DATA
select * from @bucket
select * from @incoming
select * from @IncomingSplitBucket --(sample output)

--DATA Check - The SUM AmountBucket of Grouped BucketSeqID   = the @bucket amounts see table
SELECT        CustID, BucketSeqID, SUM(AmountBucket) AS BucketCheck
FROM            @IncomingSplitBucket
GROUP BY CustID, BucketSeqID
order by 1,2

--DATA Check - The SUM AmountBucket of Grouped IncomingSeqID   = the @incoming amounts see table
SELECT        CustID, IncomingSeqID, SUM(AmountBucket) AS BucketCheck
FROM            @IncomingSplitBucket
GROUP BY CustID, IncomingSeqID
order by 1,2

更新的复杂性请求: (10/12/2019)

谢谢

--===========================================
--t1 - BUCKETS
--===========================================
DECLARE @bucket TABLE (
CustID INT,
BucketSeqID char(1),
Amount money
)

INSERT INTO @bucket
SELECT 1,'a', '1000' union
SELECT 1,'b', '1000' union
SELECT 1,'c', '2000' union
SELECT 1,'d', '2000' union

SELECT 2,'a', '5000'union
SELECT 2,'b', '5000'union
SELECT 2,'c', '1000'union
SELECT 2,'d', '1000'union

SELECT 3,'a', '5000' union
SELECT 3,'b', '5000' 
--===========================================
--t2 - PAYMENTS 
--===========================================
DECLARE @incoming TABLE (
CustID INT,
IncomingSeqID INT,
Amount money
)

INSERT INTO @incoming
SELECT 1,1, '1000' union
SELECT 1,2, '2000' union
SELECT 1,3, '3000' union

SELECT 2,1, '5000' union
SELECT 2,2, '3000' union
SELECT 2,3, '2000' union
SELECT 2,4, '2000' union
SELECT 2,5, '-3000' union

SELECT 3,1, '3000' union
SELECT 3,2, '3000' union
SELECT 3,3, '3000' union
SELECT 3,4, '500' union
SELECT 3,5, '200' union
SELECT 3,6, '-500' union
SELECT 3,7, '800' union
SELECT 3,8, '-400' union
SELECT 3,9, '500' 



--=================================================================
--t3 - EXPECTED OUTPUT
--================================================================
DECLARE @IncomingSplitBucket TABLE (
CustID INT,
IncomingSeqID INT,
BucketSeqID char(1),
AmountBucket money
)

INSERT INTO @IncomingSplitBucket

SELECT 1,1,'a','1000' union
SELECT 1,2,'b','1000' union
SELECT 1,2,'c','1000' union
SELECT 1,3,'c','1000' union
SELECT 1,3,'d','2000' union

SELECT 2,1,'a','5000' union
SELECT 2,2,'b','3000' union
SELECT 2,3,'b','2000' union
SELECT 2,4,'c','1000' union
SELECT 2,4,'d','1000' union
SELECT 2,5,'d','-1000' union
SELECT 2,5,'c','-1000' union
SELECT 2,5,'b','-1000' union

SELECT 3,1,'a','3000' union
SELECT 3,2,'a','2000' union
SELECT 3,2,'b','1000' union
SELECT 3,3,'b','3000' union
SELECT 3,4,'b','200'  union
SELECT 3,5,'b','-500' union
SELECT 3,6,'b','800'  union
SELECT 3,7,'b','-400' union
SELECT 3,8,'b','400'  union
SELECT 3,8,'x','100'  


--=================================================================
--Outputs and Data Checks
--================================================================

--REVIEW DATA
select * from @bucket
select * from @incoming
select * from @IncomingSplitBucket --(expected output)

--DATA Check - The SUM AmountBucket of Grouped BucketSeqID   = the @bucket amounts see table
SELECT        CustID, BucketSeqID, SUM(AmountBucket) AS BucketCheck
FROM            @IncomingSplitBucket
GROUP BY CustID, BucketSeqID
order by 1,2

--DATA Check - The SUM AmountBucket of Grouped IncomingSeqID   = the @incoming amounts see table
SELECT        CustID, IncomingSeqID, SUM(AmountBucket) AS BucketCheck
FROM            @IncomingSplitBucket
GROUP BY CustID, IncomingSeqID
order by 1,2

首先,我将使用一个常见的 table 表达式 (cte) 来更改列名,因此我在 table 之间没有相似的列名,以使我们的生活更轻松,加上将转换存储桶名称为简单起见,a、b、c、d 到 1、2、3、4 的序列。

然后我将继续使用另一个递归 cte 来获取第一个桶和第一个收到的付款,如果下一个记录桶没有被填充我将使用相同的未填充桶否则我将使用下一个桶,相同如果收款适合下一行存储桶的剩余部分,我将转到下一笔付款,否则我将使用剩余的收款,直到所有收款完成。

请看下面的CTE

;with bucket as (
    select CustID BucketCustID,BucketSeqID,
    case BucketSeqID when 'a' then 1 when 'b' then 2 when 'c' then 3 when 'd' then 4 end BucketSeq
    ,Amount bucketAmount from @bucket
),incoming as (
    select CustID IncomingCustID,   IncomingSeqID   ,Amount [IncomingAmount] from @incoming
),result as (
    select BucketCustID,IncomingSeqID,BucketSeqID,BucketSeq
        ,case when bucketAmount<IncomingAmount then 0 else bucketAmount-IncomingAmount end bucketAmount
        ,case when bucketAmount>IncomingAmount then 0 else IncomingAmount-bucketAmount end IncomingAmount
        ,case when bucketAmount>IncomingAmount then IncomingAmount else bucketAmount end InBucket
    from bucket b 
        inner join incoming i on i.IncomingCustID=b.BucketCustID and i.IncomingSeqID=1
    where b.BucketSeq=1
    union all
    select BucketCustID,IncomingSeqID,BucketSeqID,BucketSeq
        ,case when bucketAmount<IncomingAmount then 0 else bucketAmount-IncomingAmount end bucketAmount
        ,case when bucketAmount>IncomingAmount then 0 else IncomingAmount-bucketAmount end IncomingAmount
        ,case when bucketAmount>IncomingAmount then IncomingAmount else bucketAmount end InBucket
        from (
            select 
                b.BucketCustID,i.IncomingSeqID,b.BucketSeqID,b.BucketSeq
                ,case when r.BucketSeq=b.BucketSeq then r.bucketAmount else b.bucketAmount end bucketAmount
                ,case when r.IncomingSeqID=i.IncomingSeqID then r.IncomingAmount else i.IncomingAmount end IncomingAmount
            from result r
            inner join bucket b on b.BucketCustID=r.BucketCustID and b.BucketSeq=r.BucketSeq+(case when r.bucketAmount=0 then 1 else 0 end)
            inner join incoming i on i.IncomingCustID=r.BucketCustID and i.IncomingSeqID=r.IncomingSeqID+(case when r.IncomingAmount=0 then 1 else 0 end)
            ) Prev
)
select BucketCustID CustID,IncomingSeqID,BucketSeqID,InBucket AmountBucket
 from result r 
order by BucketCustID,IncomingSeqID,BucketSeqID

输出符合您想要的输出如下:-

CustID  IncomingSeqID   BucketSeqID AmountBucket
1       1               a           1000.00
1       2               b           1000.00
1       2               c           1000.00
1       3               c           1000.00
1       3               d           2000.00
2       1               a           5000.00
2       2               b           3000.00
2       3               b           2000.00
2       4               c           1000.00
2       4               d           1000.00
3       1               a           3000.00
3       2               a           2000.00
3       2               b           1000.00
3       3               b           3000.00
3       4               b           1000.00