5

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

例如,我的表包含

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

数据是以这种方式导入的,但我需要根据也导入的支付交易来填充Current和列。Paid

是否有一种简单的方法可以编写查询来确定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

结果:

已开票的当前
100 0 100
100 0 200
100 0 300
0 250 50
0 25 25

我确信有办法做到这一点,但现在我的大脑似乎在罢工并且拒绝提出解决方案。

4

2 回答 2

4

我想我找到了解决方案

首先,我不需要将付费交易链接到发票交易,所以我只需要所有付款的总和

select accountid, sum(paid)
from mytable
where type = 'Payment'
group by accountid

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

为此,我修改了我的运行总查询,因此它只对费用求和,而不是对费用和付款进行求和

;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

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

从那里,我只需要一份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

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

Id AccountId Invoiced 已付 当前
1 1 100 100 0
2 1 100 100 0
3 1 100 75 25
于 2012-09-06T18:29:47.357 回答
0

您可以计算已付费列和当前列的运行总计和总和,然后执行一些数学运算以获取当前列值。我试了一下,但在计算最终值时陷入了困境。这不是一个简单的方法,但它是一种方法。

DECLARE @myTable TABLE
(
    [TranId] INT,
    [Type] VARCHAR(10),
    [Invoiced] INT,
    [Paid] INT,
    [Current] INT,
    [SumPaid] INT,
    [SumCurrent] INT,
    [RunningPaid] INT,
    [RunningCurrent] INT
)

INSERT INTO @myTable SELECT 1, 'Charge', 100, 0, 100, null, null, null, null
INSERT INTO @myTable SELECT 2, 'Charge', 100, 0, 100, null, null, null, null
INSERT INTO @myTable SELECT 3, 'Charge', 100, 0, 100, null, null, null, null
INSERT INTO @myTable SELECT 4, 'Paid', 0, 250, 0, null, null, null, null
INSERT INTO @myTable SELECT 5, 'Paid', 0, 25, 0, null, null, null, null


UPDATE @myTable SET SumPaid = (SELECT SUM([Paid]) FROM @myTable)
UPDATE @myTable SET SumCurrent = (SELECT SUM([Current]) FROM @myTable)
UPDATE @myTable
SET 
    [RunningPaid] = full_running_total_set.[RunningPaid], 
    [RunningCurrent] = full_running_total_set.[RunningCurrent]
FROM @myTable
INNER JOIN
(
    SELECT
        TranId1,
        SUM([Paid]) AS [RunningPaid],
        SUM([Current]) AS [RunningCurrent]
    FROM
    (
        SELECT
            set2.[Paid],
            set2.[Current],
            set1.[TranId] AS [TranId1],
            set2.[TranId] AS [TranId2]
        FROM @myTable set1
        INNER JOIN @myTable set2
            ON set1.[TranId] >= set2.[TranId]
    )running_total_set
    GROUP BY [TranId1]
)full_running_total_set
ON [TranId] = full_running_total_set.[TranId1]

SELECT * FROM @myTable
于 2012-09-06T18:02:07.677 回答