SQL累积值

lso*_*ira 3 sql sql-server sql-server-2008

我正在编写一个SQLServer 2008存储过程,它接受一个付款表,并尝试根据相关表中描述的一组规则(基本上是一组存储桶)分配这些付款.然而,分配(将支付价值放入桶中)正是导致我头痛的原因.

假设表Payments包含要支付的值和表Buckets是关于应该在每个桶中放入多少钱,直到要支付的初始值耗尽(达到0).

使用下面的表作为示例(实际用例有点做作,因为有一些复杂的标准来选择适合每次付款的存储桶):

PaymentId     Value                 BucketId       MaxAmount
--------------------------          --------------------------------
1             16.5                  1              5.5
2             7.0                   2              10
                                    3              8.3
Run Code Online (Sandbox Code Playgroud)

对于付款1:5.5单位(该桶的最大值)应进入桶1,10个单位进入桶2(11.5是桶1的剩余量,但仍超过桶2的最大值)和1个单位(16.5 - 5.5 - 10) )应该放入桶3.重复所有付款.

这很容易在任何命令式语言中实现,甚至可能在带有for/while循环的SQL中实现,但我试图意识到是否有更好的方法(即使它是非可移植的并且特定于SQLServer 2005+).

我已经做了一些研究(主要是递归CTE),但没有真正想到的东西.我确信有很多StackOverflowers和SQL-fu一起回答它们的问题,所以我想把它放在那里看看......

非常感谢你的帮助.

Gul*_*eel 5

将您的存储桶表放入临时表,然后有一个名为running total的额外列.这将有运行总计直到此连接,然后交叉加入付款和tempbucket表并指定支付<=运行totalin tempbucket表的条件.这应该可以解决您的问题.我已经使用Mafu Josh的DDL来创建以下查询,这要归功于他.我希望OP应该总是发布这些东西,以使其他人的生活更轻松.

看起来这个buckte表非常小.但是如果它是非常大的表.然后生成运行总计使用带变量的更新.这比下面的方法更有效.对我来说听起来这个表或多或少是静态的表,因此您可以将运行总计作为表本身的一部分.

   DECLARE @Buckets TABLE ( 
    BucketId INT, 
    MaxAmount DECIMAL(18,6) 
) 

INSERT INTO @Buckets VALUES (1, 5.5) 
INSERT INTO @Buckets VALUES (2, 10) 
INSERT INTO @Buckets VALUES (3, 8.3) 

DECLARE @Payments TABLE ( 
    PaymentId INT, 
    Value DECIMAL(18,6) 
) 

INSERT INTO @Payments VALUES (1,16.5) 
INSERT INTO @Payments VALUES (2,7.0) 
INSERT INTO @Payments VALUES (3,23.8) 

DECLARE @tempBuckets TABLE ( 
    BucketId INT, 
    MaxAmount DECIMAL(18,6) ,
    currentruntotal decimal(18,6)
) 
insert into @tempBuckets select bucketid,maxamount ,(select SUM(maxamount) from @Buckets bin where b.bucketid >=bin.bucketid)
--,isnull((select SUM(maxamount) from @Buckets bin where b.bucketid > bin.bucketid),0)
from @Buckets b

select * from @tempBuckets
select PaymentId,Value,BucketId,
case when p.Value >= tb.currentruntotal then tb.MaxAmount else p.Value - tb.currentruntotal + tb.MaxAmount end as bucketamount
from @Payments p inner join @tempBuckets tb on  (p.Value >= tb.currentruntotal or p.Value between tb.currentruntotal - tb.MaxAmount and tb.currentruntotal )
order by PaymentId
go
Run Code Online (Sandbox Code Playgroud)