如何在没有游标的情况下将值分布在多行中?

Rac*_*hel 5 sql t-sql sql-server-2005

有没有一种简单的方法可以将值分布在多行中?

例如,我的表包含

类型已开票已付电流
充电 100 0 100
充电 100 0 100
充电 100 0 100
付款 0 250 0
付款 0 25 0

数据是通过这种方式导入的,但我需要根据也导入的付款交易为该交易填充CurrentPaid列。

有没有一种简单的方法来编写查询来确定Current每条记录的列的余额?

例如,250 会将 100 应用于前两条记录,50 应用于接下来的两条记录,而 25 将应用于最后一条,因此更新Current表中的余额后的最终结果应该是:

类型已开票已付电流
充电 100 100 0
充电 100 100 0
充电 100 75 25
付款 0 250 0
付款 0 25 0

理想情况下,我希望使用单个查询来执行此操作,而不是使用游标来单独处理每个项目。我一直在尝试通过使用Row_Number()函数并加入两个子查询来做到这一点,但我知道我在这里遗漏了一些东西

这是我的第一次尝试,结果得到了当前余额的运行总数

;with cte(invoiced, paid, current)
as (
    select invoiced, paid, current
        , row_number() over (order by datecreated)
    from mytable
)

select t1.invoiced, t1.paid, sum(t2.invoiced - t2.paid) as [current]
from cte as t1
join cte as t2 on t1.number = t2.number and t2.rownum <= t1.rownum
group by t1.uid, t1.number, t1.rownum
order by t1.rownum
Run Code Online (Sandbox Code Playgroud)

结果:

已开具发票的已付现款
100 0 100
100 0 200
100 0 300
0 250 50
0 25 25

我确信有办法做到这一点,但现在我的大脑似乎受到了打击,拒绝提出解决方案。

Rac*_*hel 4

我想我找到了解决方案

首先,我不需要将付费交易链接到已开票交易,因此我只需要所有付款的总和

select accountid, sum(paid)
from mytable
where type = 'Payment'
group by accountid
Run Code Online (Sandbox Code Playgroud)

然后我需要将此值应用于每条记录,直到运行总额大于支付总额。

为此,我修改了运行总计查询,使其仅对费用求和,而不是对费用和付款求和

;with cte(id, accountid, invoiced, paid, current)
as (
    select id, accountid, invoiced, paid, current
        , row_number() over (order by datecreated)
    from mytable
    where type = 'Charge'
)

select t1.id, t1.accountid, t1.invoiced, sum(t2.invoiced) as [runningTotalOfCharges]
from cte as t1
join cte as t2 on t1.number = t2.number and t2.rownum <= t1.rownum
group by t1.id, t1.accountid, t1.invoiced
Run Code Online (Sandbox Code Playgroud)

并将其加入到付款查询中,所以现在我有一堆行,其中包含总付款金额、截至该记录的费用运行总额以及当前记录的费用金额。

从那里,我只需要一份CASE声明来确定费用是否已全额支付、部分支付或根本未支付,并使用一点数学来计算出PaidCurrent记录

select charged.Id, charged.AccountId, charged.Invoiced
    -- Use Case statements to determine if this payment is fully paid, partially paid, 
    -- or not paid at all, then determine Current and Paid based on that
    , case when totalpaid - runningtotal >= 0 then invoiced 
        when invoiced > abs(totalpaid - runningtotal) then invoiced + totalpaid - runningtotal
        else 0 end as [Paid]
    , case when totalpaid - runningtotal >= 0 then 0 
        when invoiced > abs(totalpaid - runningtotal) then abs(totalpaid - runningtotal)
        else invoiced end as [Current]
from 
(
    -- Running total query from above
    select t1.id, t1.accountid, t1.invoiced, sum(t2.invoiced) as [runningtotal]
    from cte as t1
    join cte as t2 on t1.number = t2.number and t2.rownum <= t1.rownum
    group by t1.id, t1.accountid, t1.invoiced
) as charged

inner join (
    -- Total Paid query from above
    select accountid, sum(paid) as totalpaid
    from mytable
    where type = 'Payment'
    group by accountid
) as paid on charged.number = paid.number
Run Code Online (Sandbox Code Playgroud)

而最终的结果就是我想要的。只需要通过Id列将其连接到实际数据表,并更新PaidCurrent值:)

ID AccountId 已开具发票 已付款 当前
1 1 100 100 0
2 1 100 100 0
3 1 100 75 25