如何在没有游标的情况下在多行中传播值?

时间:2012-09-06 15:48:36

标签: sql tsql sql-server-2005

是否有一种简单的方法可以跨多行传播值?

例如,我的表包含

Type      Invoiced    Paid    Current
Charge    100         0       100
Charge    100         0       100
Charge    100         0       100
Payment   0         250       0
Payment   0          25       0

以这种方式导入数据,但我需要根据同时导入的付款交易,在CurrentPaid列填充该交易的任何内容。

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

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

Type      Invoiced    Paid    Current
Charge    100         100     0
Charge    100         100     0
Charge    100          75     25
Payment   0           250     0
Payment   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

结果:

Invoiced    Paid    Current
100         0       100
100         0       200
100         0       300
0         250       50
0          25       25

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

2 个答案:

答案 0 :(得分:3)

我想我找到了解决方案

首先,我不需要将付费交易与发票交易相关联,因此我只需要所有付款的总和

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声明来确定费用是完全支付,部分支付还是根本不支付,并使用一点数学来计算Paid和{ {1}}记录

Current

最终的结果就是我想要的。只需通过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 列将其加入实际数据表,然后更新IdPaid值:)

Id    AccountId    Invoiced    Paid    Current
1     1            100         100     0
2     1            100         100     0
3     1            100         75      25

答案 1 :(得分: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